| #!/usr/bin/env python |
| # Copyright (c) 2012 The Chromium Authors. All rights reserved. |
| # Use of this source code is governed by a BSD-style license that can be |
| # found in the LICENSE file. |
| |
| """Unit tests for verification/try_server.py.""" |
| |
| import json |
| import logging |
| import optparse |
| import os |
| import re |
| import StringIO |
| import sys |
| import time |
| import unittest |
| import urllib |
| |
| ROOT_DIR = os.path.dirname(os.path.abspath(__file__)) |
| sys.path.insert(0, os.path.join(ROOT_DIR, '..')) |
| |
| # In tests/ |
| import mocks # pylint: disable=W0403 |
| |
| from testing_support import auto_stub |
| |
| # In root |
| import buildbot_json |
| from verification import base |
| from verification import try_server |
| |
| # pylint: disable=W0212 |
| SUCCESS = buildbot_json.SUCCESS |
| WARNINGS = buildbot_json.WARNINGS |
| FAILURE = buildbot_json.FAILURE |
| SKIPPED = buildbot_json.SKIPPED |
| EXCEPTION = buildbot_json.EXCEPTION |
| |
| |
| class FakeTryServer(auto_stub.SimpleMock): |
| """Stateful try server mock. |
| |
| Includes calls to send try jobs TryChange() and visible results from HTTP |
| requests. |
| """ |
| def __init__(self, unit_test): |
| super(FakeTryServer, self).__init__(unit_test) |
| |
| # Try server immutable properties. |
| self.steps = ['update', 'compile', 'test1', 'test2'] |
| self.server_url = 'http://foo/bar' |
| |
| # State of the try server. |
| self._builds = { 'linux': [], 'mac': [] } |
| self.pending_builds = { |
| 'linux': [], |
| 'mac': [] |
| } |
| # Default mocks. |
| self.unit_test.mock(urllib, 'urlopen', self._mockurlopen) |
| self.unit_test.mock(try_server.trychange, 'TryChange', self.TryChangeMock) |
| |
| def TryChangeMock(self, cmd, _change, swallow_exception): |
| """Mocks trychange.py.""" |
| self.assertEqual(swallow_exception, True) |
| parser = optparse.OptionParser() |
| parser.add_option('--bot', action='append') |
| parser.add_option('--clobber', action='store_true', default=False) |
| parser.add_option('--email') |
| parser.add_option('--issue', type='int') |
| parser.add_option('--name') |
| parser.add_option('--no_search', action='store_true') |
| parser.add_option('--patchset', type='int') |
| parser.add_option('--revision') |
| parser.add_option('--rietveld_url') |
| parser.add_option('--user') |
| options, args = parser.parse_args(cmd) |
| self.assertEqual(options.email, 'user1@example.com') |
| self.assertEqual(options.issue, 42) |
| self.assertEqual(options.no_search, True) |
| self.assertEqual(options.patchset, 23) |
| self.assertEqual( |
| options.rietveld_url, |
| '%s/download/issue42_23.diff' % self.unit_test.context.rietveld.url) |
| self.assertEqual(options.user, 'user1') |
| self.assertEqual(args, ['extra_flags']) |
| bot = ', '.join(options.bot) |
| call = 'trychange b={%s} c=%s r=%s' % ( |
| bot, options.clobber, options.revision) |
| if options.name != '42-23': |
| call += ' n=%s' % options.name |
| self.calls.append(call) |
| logging.debug(self.calls[-1]) |
| |
| def _mockurlopen_internal(self, sub_url): |
| """Returns data to be encoded before returning.""" |
| # sub_url is str on python <= 2.6 and unicode for >= 2.7 |
| self.unit_test.assertTrue(isinstance(sub_url, basestring)) |
| expected_url = self.server_url + '/json/' |
| self.unit_test.assertTrue(sub_url.startswith(expected_url)) |
| self.calls.append(sub_url[len(expected_url):]) |
| baseurl = '^%s/json/' % re.escape(self.server_url) |
| match = re.match(baseurl + r'builders/(\w+)/builds/\?(.+)$', sub_url) |
| if match: |
| data = {} |
| for query in match.group(2).split('&'): |
| m = re.match(r'select=(\w+)', query) |
| self.unit_test.assertTrue(m, (match.group(2), query)) |
| build = int(m.group(1)) |
| data[str(build)] = self._builds[match.group(1)][build] |
| return data |
| |
| match = re.match(baseurl + r'builders/(\w+)/builds/_all$', sub_url) |
| if match: |
| # Data is not stored exactly as the try server serves it. |
| data = {} |
| for i, build in enumerate(self._builds[match.group(1)]): |
| data[str(i)] = build |
| return data |
| |
| match = re.match(baseurl + r'builders/\?(.+)$', sub_url) |
| if match: |
| data = {} |
| for query in match.group(1).split('&'): |
| m = re.match(r'select=(\w+)', query) |
| self.unit_test.assertTrue(m, (match.group(1), query)) |
| builder = m.group(1) |
| data[builder] = { |
| 'cachedBuilds': range(len(self._builds[builder])), |
| 'pendingBuilds': len(self.pending_builds.get(builder, [])), |
| } |
| return data |
| |
| match = re.match(baseurl + r'builders/(\w+)/pendingBuilds$', sub_url) |
| if match: |
| return self.pending_builds[match.group(1)] |
| |
| def _mockurlopen(self, sub_url): |
| """Mocks urllib.urlopen() and JSON encode + StringIO buffers.""" |
| sub_url = re.match(r'^(.+)[\&\?]filter=1$', sub_url).group(1) |
| data = self._mockurlopen_internal(sub_url) |
| self.unit_test.assertNotEquals(None, data, sub_url) |
| #logging.debug('_mockurlopen(%s) -> %s' % (sub_url, data)) |
| return StringIO.StringIO(json.dumps(data)) |
| |
| def add_build(self, builder, revision, reason, step_results): |
| """Add a build to a builder.""" |
| self.assertEqual(len(self.steps), len(step_results)) |
| assert isinstance(revision, (str, int)) |
| data = { |
| 'reason': reason or self.unit_test.pending.pending_name(), |
| 'sourceStamp': { |
| 'revision': 'sol@%s' % revision, |
| 'hasPatch': True, |
| }, |
| 'steps': [], |
| 'blame': ['user1@example.com'], |
| 'number': 0, |
| 'slave': 'foo', |
| } |
| result = max(step_results) |
| if result in (SUCCESS, WARNINGS) and step_results[-1] is None: |
| result = None |
| for i, step in enumerate(self.steps): |
| data['steps'].append({ |
| 'name': step, |
| 'results': [step_results[i]], |
| }) |
| data['results'] = [result] |
| self._builds[builder].append(data) |
| |
| def set_build_result(self, builder, result): |
| """Override the build result for every steps to |result|.""" |
| for step in self._builds[builder][-1]['steps']: |
| step['results'] = [result] |
| self._builds[builder][-1]['results'] = [result] |
| |
| |
| class TryServerSvnTest(mocks.TestCase): |
| def setUp(self): |
| super(TryServerSvnTest, self).setUp() |
| self.email = 'user1@example.com' |
| self.user = 'user1' |
| self.timestamp = [1.] |
| # Mocks http://chromium-status.appspot.com/lkgr |
| self.lkgr = 123 |
| self.try_server = FakeTryServer(self) |
| self.mock(time, 'time', lambda: self.timestamp[-1]) |
| |
| try_server.TryRunnerSvn.update_latency = 0 |
| self.builders_and_tests = { |
| 'linux': ['test1', 'test2'], |
| 'mac': ['test1', 'test2'], |
| } |
| self.try_runner = try_server.TryRunnerSvn( |
| self.context, |
| self.try_server.server_url, |
| self.email, |
| self.builders_and_tests, |
| ['ignored_step'], |
| 'sol', |
| ['extra_flags',], |
| lambda: self.lkgr, |
| ) |
| self.pending.revision = 123 |
| |
| def tearDown(self): |
| try: |
| if not self.has_failed(): |
| self.try_server.check_calls([]) |
| finally: |
| super(TryServerSvnTest, self).tearDown() |
| |
| def get_verif(self): |
| return self.pending.verifications[self.try_runner.name] |
| |
| def assertPending( |
| self, state, nb_jobs, error_message, |
| linux_build=1, |
| mac_build=1, |
| linux_state=None, |
| mac_state=None, |
| linux_clobber=False, |
| mac_clobber=False, |
| linux_rev=123, |
| mac_rev=123, |
| linux_sent=1, |
| mac_sent=1, |
| linux_name='42-23', |
| mac_name='42-23'): |
| if linux_state is None: |
| linux_state = state |
| if mac_state is None: |
| mac_state = state |
| self.assertEqual([self.try_runner.name], self.pending.verifications.keys()) |
| self.assertEqual(error_message, self.get_verif().error_message) |
| self.assertEqual(nb_jobs, len(self.get_verif().try_jobs)) |
| self.assertEqual( |
| len(self.builders_and_tests), len(self.get_verif().try_jobs)) |
| self.assertEqual(linux_name, self.get_verif().try_jobs[0].name) |
| self.assertEqual('linux', self.get_verif().try_jobs[0].builder) |
| self.assertEqual(linux_rev, self.get_verif().try_jobs[0].revision) |
| self.assertEqual(linux_sent, self.get_verif().try_jobs[0].sent) |
| self.assertEqual(linux_clobber, self.get_verif().try_jobs[0].clobber) |
| self.assertEqual(linux_build, self.get_verif().try_jobs[0].build) |
| self.assertEqual(linux_state, self.get_verif().try_jobs[0].get_state()) |
| if len(self.builders_and_tests) > 1: |
| self.assertEqual(mac_name, self.get_verif().try_jobs[1].name) |
| self.assertEqual('mac', self.get_verif().try_jobs[1].builder) |
| self.assertEqual(mac_rev, self.get_verif().try_jobs[1].revision) |
| self.assertEqual(mac_sent, self.get_verif().try_jobs[1].sent) |
| self.assertEqual(mac_clobber, self.get_verif().try_jobs[1].clobber) |
| self.assertEqual(mac_build, self.get_verif().try_jobs[1].build) |
| self.assertEqual(mac_state, self.get_verif().try_jobs[1].get_state()) |
| self.assertEqual(state, self.get_verif().get_state()) |
| |
| def testVoid(self): |
| self.assertEqual(self.pending.verifications.keys(), []) |
| |
| def testVerificationVoid(self): |
| self.try_runner.verify(self.pending) |
| self.assertPending(base.PROCESSING, 2, None, linux_build=None, |
| mac_build=None) |
| self.try_server.check_calls( |
| ['trychange b={linux:test1,test2, mac:test1,test2} c=False r=sol@123']) |
| self.context.status.check_names(['try server'] * 2) |
| |
| def testVoidUpdate(self): |
| self.try_runner.update_status([]) |
| |
| def test_steps_quality(self): |
| self.assertEqual(None, try_server.steps_quality([])) |
| self.assertEqual(True, try_server.steps_quality([True, None])) |
| self.assertEqual(False, try_server.steps_quality([True, None, False])) |
| |
| def testStepQualityNone(self): |
| self.try_runner.status.builders['linux'].builds.cache() |
| self.assertEqual( |
| (None, 0), |
| self.try_runner.step_db.revision_quality_builder_steps('linux', 123)) |
| self.try_server.check_calls(['builders/linux/builds/_all']) |
| |
| def testStepQualityGood(self): |
| self.try_server.add_build( |
| 'linux', 123, None, [SUCCESS, None, None, None]) |
| self.try_runner.status.builders['linux'].builds.cache() |
| self.try_server.check_calls(['builders/linux/builds/_all']) |
| self.assertEqual( |
| ([True, None, None, None], 1), |
| self.try_runner.step_db.revision_quality_builder_steps('linux', 123)) |
| self.try_server.set_build_result('linux', SUCCESS) |
| self.try_runner.status.builders['linux'].builds.refresh() |
| self.assertEqual( |
| ([True] * 4, 1), |
| self.try_runner.step_db.revision_quality_builder_steps('linux', 123)) |
| self.try_server.check_calls(['builders/linux/builds/_all']) |
| |
| def testStepQualityBad(self): |
| self.try_server.add_build( |
| 'linux', 123, None, [SUCCESS, SUCCESS, FAILURE, SUCCESS]) |
| self.try_runner.status.builders['linux'].builds.cache() |
| # Also test that FakeTryServer.add_build() is implemented correctly. |
| self.assertEqual( |
| ([True, True, False, True], 1), |
| self.try_runner.step_db.revision_quality_builder_steps('linux', 123)) |
| self.try_server.check_calls(['builders/linux/builds/_all']) |
| |
| def testStepQualityBadIncomplete(self): |
| self.try_server.add_build( |
| 'linux', 123, None, [SUCCESS, SUCCESS, FAILURE, None]) |
| self.try_runner.status.builders['linux'].builds.cache() |
| # Also test that FakeTryServer.add_build() is implemented correctly. |
| self.assertEqual( |
| ([True, True, False, None], 1), |
| self.try_runner.step_db.revision_quality_builder_steps('linux', 123)) |
| self.try_server.check_calls(['builders/linux/builds/_all']) |
| |
| def testStepQualityGoodAndBad(self): |
| self.try_server.add_build( |
| 'linux', 123, None, [SUCCESS, SUCCESS, SUCCESS, SUCCESS]) |
| self.try_server.add_build('linux', 123, None, [FAILURE, None, None, None]) |
| self.try_runner.status.builders['linux'].builds.cache() |
| self.try_server.check_calls(['builders/linux/builds/_all']) |
| self.assertEqual( |
| ([True] * 4, 2), |
| self.try_runner.step_db.revision_quality_builder_steps('linux', 123)) |
| |
| def testQualityAutomatic(self): |
| self.try_runner.verify(self.pending) |
| self.assertEqual( |
| (None, 0), |
| self.try_runner.step_db.revision_quality_builder_steps( |
| 'linux', 123)) |
| self.try_server.add_build( |
| 'linux', 123, 'georges tried stuff', |
| [SUCCESS, SUCCESS, SUCCESS, SUCCESS]) |
| self.try_runner.update_status([self.pending]) |
| self.assertEqual( |
| ([True] * 4, 1), |
| self.try_runner.step_db.revision_quality_builder_steps( |
| 'linux', 123)) |
| self.try_server.check_calls( |
| [ 'trychange b={linux:test1,test2, mac:test1,test2} c=False r=sol@123', |
| 'builders/?select=linux&select=mac', |
| 'builders/linux/builds/_all', 'builders/mac/builds/_all']) |
| self.context.status.check_names(['try server'] * 2) |
| |
| def testQualityManual(self): |
| self.try_server.add_build( |
| 'linux', 123, 'georges tried stuff', |
| [SUCCESS, SUCCESS, SUCCESS, SUCCESS]) |
| self.try_runner.status.builders['linux'].builds.cache() |
| self.assertEqual( |
| ([True] * 4, 1), |
| self.try_runner.step_db.revision_quality_builder_steps( |
| 'linux', 123)) |
| self.try_server.check_calls(['builders/linux/builds/_all']) |
| |
| def _simple(self, status_linux, status_mac=None, error_msg=None): |
| """status_linux affects test1, status_mac affects test2.""" |
| def is_failure(status): |
| return status in (FAILURE, EXCEPTION) |
| |
| self.assertEqual( |
| bool(is_failure(status_linux) or is_failure(status_mac)), |
| bool(error_msg)) |
| if status_mac is None: |
| status_mac = status_linux |
| self.lkgr = 12 |
| self.try_server.add_build( |
| 'linux', 123, None, [SUCCESS, SUCCESS, SUCCESS, SUCCESS]) |
| self.try_server.add_build( |
| 'mac', 123, None, [SUCCESS, SUCCESS, SUCCESS, SUCCESS]) |
| |
| self.try_runner.verify(self.pending) |
| self.try_server.check_calls( |
| ['trychange b={linux:test1,test2, mac:test1,test2} c=False r=sol@123']) |
| self.try_server.add_build( |
| 'linux', 123, None, [SUCCESS, SUCCESS, status_linux, SUCCESS]) |
| self.try_server.add_build( |
| 'mac', 123, None, [SUCCESS, SUCCESS, SUCCESS, status_mac]) |
| self.try_runner.update_status([self.pending]) |
| |
| if is_failure(status_linux): |
| self.assertEqual(123, self.try_runner.get_lkgr('linux')) |
| self.assertEqual( |
| 123, self.try_runner.step_db.last_good_revision_builder('linux')) |
| else: |
| self.assertEqual(123, self.try_runner.get_lkgr('linux')) |
| self.assertEqual( |
| 123, self.try_runner.step_db.last_good_revision_builder('linux')) |
| if is_failure(status_mac): |
| self.assertEqual(123, self.try_runner.get_lkgr('mac')) |
| self.assertEqual( |
| 123, self.try_runner.step_db.last_good_revision_builder('mac')) |
| else: |
| self.assertEqual(123, self.try_runner.get_lkgr('mac')) |
| self.assertEqual( |
| 123, self.try_runner.step_db.last_good_revision_builder('mac')) |
| |
| if error_msg: |
| # Can't test failure without testing automatic retry mechanism. |
| expected = ( |
| [ 'builders/?select=linux&select=mac', |
| 'builders/linux/builds/_all', 'builders/mac/builds/_all']) |
| |
| if is_failure(status_linux): |
| expected.append( |
| 'trychange b={linux:test1} c=False r=sol@123 n=42-23 (retry)') |
| linux_build = None |
| linux_state = base.PROCESSING |
| linux_name = '42-23 (retry)' |
| else: |
| linux_build = 1 |
| linux_state = base.SUCCEEDED |
| linux_name = '42-23' |
| |
| if is_failure(status_mac): |
| expected.append( |
| 'trychange b={mac:test2} c=False r=sol@123 n=42-23 (retry)') |
| mac_build = None |
| mac_state = base.PROCESSING |
| mac_name = '42-23 (retry)' |
| else: |
| mac_build = 1 |
| mac_state = base.SUCCEEDED |
| mac_name = '42-23' |
| |
| self.assertPending( |
| base.PROCESSING, 2, None, linux_build=linux_build, |
| mac_build=mac_build, linux_state=linux_state, mac_state=mac_state, |
| linux_name=linux_name, mac_name=mac_name) |
| self.try_server.check_calls(expected) |
| |
| if is_failure(status_linux): |
| self.try_server.add_build( |
| 'linux', 123, linux_name, [SUCCESS, SUCCESS, status_linux, SUCCESS]) |
| if is_failure(status_mac): |
| self.try_server.add_build( |
| 'mac', 123, mac_name, [SUCCESS, SUCCESS, SUCCESS, status_mac]) |
| |
| self.try_runner.update_status([self.pending]) |
| if is_failure(status_linux): |
| linux_state = base.FAILED |
| linux_build = 2 |
| else: |
| linux_build = 1 |
| if is_failure(status_mac): |
| mac_state = base.FAILED |
| mac_build = 2 |
| else: |
| mac_build = 1 |
| self.assertPending( |
| base.FAILED, 2, error_msg, |
| linux_build=linux_build, mac_build=mac_build, |
| linux_state=linux_state, mac_state=mac_state, |
| linux_name=linux_name, mac_name=mac_name) |
| |
| if is_failure(status_linux) and is_failure(status_mac): |
| self.try_server.check_calls( |
| [ 'builders/?select=linux&select=mac', |
| 'builders/linux/builds/_all', 'builders/mac/builds/_all']) |
| self.context.checkout.check_calls( |
| [ 'prepare(123)', |
| 'apply_patch(%r)' % self.context.rietveld.patchsets[-2], |
| 'prepare(123)', |
| 'apply_patch(%r)' % self.context.rietveld.patchsets[-1]]) |
| elif is_failure(status_linux): |
| self.try_server.check_calls( |
| ['builders/?select=linux', 'builders/linux/builds/_all']) |
| self.context.checkout.check_calls( |
| [ 'prepare(123)', |
| 'apply_patch(%r)' % self.context.rietveld.patchsets[-1]]) |
| else: |
| self.try_server.check_calls( |
| ['builders/?select=mac', 'builders/mac/builds/_all']) |
| self.context.checkout.check_calls( |
| [ 'prepare(123)', |
| 'apply_patch(%r)' % self.context.rietveld.patchsets[-1]]) |
| else: |
| self.assertPending(base.SUCCEEDED, 2, None) |
| self.try_server.check_calls( |
| [ 'builders/?select=linux&select=mac', |
| 'builders/linux/builds/_all', 'builders/mac/builds/_all']) |
| count = 6 + 3 * ( |
| int(is_failure(status_linux)) + int(is_failure(status_mac))) |
| self.context.status.check_names(['try server'] * count) |
| |
| def testImmediateSuccess(self): |
| self._simple(SUCCESS) |
| |
| def testImmediateWarnings(self): |
| self._simple(WARNINGS) |
| |
| def testImmediateSkipped(self): |
| self._simple(SKIPPED) |
| |
| def second_fail_msg( |
| self, clname, step2, step1, builder, number, is_clobber=False): |
| extra = '' |
| if is_clobber: |
| extra = ' (clobber build)' |
| return ( |
| u'Try job failure for %s on %s for step "%s"%s.\n' |
| u'It\'s a second try, previously, step "%s" failed.\n' |
| u'%s/buildstatus?builder=%s&number=%s\n') % ( |
| clname, builder, step2, extra, step1, self.try_server.server_url, |
| builder, number) |
| def testImmediateFailureLinux(self): |
| self._simple( |
| FAILURE, SUCCESS, |
| self.second_fail_msg('42-23 (retry)', 'test1', 'test1', 'linux', 2)) |
| |
| def testImmediateFailureMac(self): |
| self._simple( |
| SUCCESS, FAILURE, |
| self.second_fail_msg('42-23 (retry)', 'test2', 'test2', 'mac', 2)) |
| |
| def testImmediateDoubleFailure(self): |
| self._simple( |
| FAILURE, FAILURE, |
| self.second_fail_msg('42-23 (retry)', 'test2', 'test2', 'mac', 2)) |
| |
| def testImmediateException(self): |
| self._simple( |
| SUCCESS, EXCEPTION, |
| self.second_fail_msg('42-23 (retry)', 'test2', 'test2', 'mac', 2)) |
| |
| def testSuccess(self): |
| self.lkgr = 2 |
| # Normal workflow with incremental success. |
| self.try_runner.verify(self.pending) |
| self.try_server.check_calls( |
| ['trychange b={linux:test1,test2, mac:test1,test2} c=False r=sol@123']) |
| |
| self.try_runner.update_status([self.pending]) |
| self.assertPending( |
| base.PROCESSING, 2, None, linux_build=None, mac_build=None) |
| self.try_server.check_calls( |
| ['builders/?select=linux&select=mac', |
| 'builders/linux/builds/_all', 'builders/mac/builds/_all']) |
| |
| self.try_server.add_build( |
| 'linux', 123, None, [SUCCESS, None, None, None]) |
| self.try_runner.update_status([self.pending]) |
| self.assertPending(base.PROCESSING, 2, None, linux_build=0, mac_build=None) |
| self.try_server.check_calls( |
| ['builders/?select=linux&select=mac', |
| 'builders/linux/builds/_all', 'builders/mac/builds/_all']) |
| |
| self.try_server.add_build( |
| 'mac', 123, None, [SUCCESS, None, None, None]) |
| self.try_runner.update_status([self.pending]) |
| self.assertPending(base.PROCESSING, 2, None, linux_build=0, mac_build=0) |
| self.try_server.check_calls( |
| ['builders/?select=mac', |
| 'builders/mac/builds/_all', 'builders/linux/builds/?select=0']) |
| |
| # This one will be cached since it's now immutable. |
| self.try_server.set_build_result('mac', SUCCESS) |
| self.try_runner.update_status([self.pending]) |
| self.assertPending( |
| base.PROCESSING, 2, None, linux_build=0, mac_build=0, |
| mac_state=base.SUCCEEDED) |
| self.try_server.check_calls( |
| ['builders/linux/builds/?select=0', 'builders/mac/builds/?select=0']) |
| |
| self.try_server.set_build_result('linux', SUCCESS) |
| self.try_runner.update_status([self.pending]) |
| self.assertPending(base.SUCCEEDED, 2, None, linux_build=0, mac_build=0) |
| self.assertEqual( |
| 123, self.try_runner.step_db.last_good_revision_builder('linux')) |
| self.assertEqual( |
| 123, self.try_runner.step_db.last_good_revision_builder('mac')) |
| self.assertEqual( |
| ([True] * 4, 1), |
| self.try_runner.step_db.revision_quality_builder_steps( |
| 'linux', 123)) |
| self.assertEqual( |
| ([True] * 4, 1), |
| self.try_runner.step_db.revision_quality_builder_steps( |
| 'mac', 123)) |
| self.try_server.check_calls(['builders/linux/builds/?select=0']) |
| self.context.status.check_names(['try server'] * 6) |
| |
| def testIgnorePreviousJobs(self): |
| self.try_runner.verify(self.pending) |
| self.try_server.check_calls( |
| ['trychange b={linux:test1,test2, mac:test1,test2} c=False r=sol@123']) |
| |
| self.try_runner.update_status([self.pending]) |
| self.try_server.check_calls( |
| [ 'builders/?select=linux&select=mac', |
| 'builders/linux/builds/_all', 'builders/mac/builds/_all']) |
| |
| self.try_server.add_build('linux', 12, None, [None, None, None, None]) |
| self.try_server.add_build('mac', 12, None, [None, None, None, None]) |
| self.try_runner.update_status([self.pending]) |
| self.assertPending( |
| base.PROCESSING, 2, None, linux_build=None, |
| mac_build=None) |
| self.try_server.check_calls( |
| ['builders/?select=linux&select=mac', |
| 'builders/linux/builds/_all', 'builders/mac/builds/_all']) |
| |
| self.try_server.add_build( |
| 'linux', 123, None, [SUCCESS, SUCCESS, SUCCESS, SUCCESS]) |
| self.try_server.add_build( |
| 'mac', 123, None, [SUCCESS, SUCCESS, SUCCESS, SUCCESS]) |
| self.try_runner.update_status([self.pending]) |
| self.assertPending(base.SUCCEEDED, 2, None) |
| self.assertEqual( |
| 123, self.try_runner.step_db.last_good_revision_builder('linux')) |
| self.assertEqual( |
| 123, self.try_runner.step_db.last_good_revision_builder('mac')) |
| self.assertEqual( |
| ([True] * 4, 1), |
| self.try_runner.step_db.revision_quality_builder_steps( |
| 'linux', 123)) |
| self.assertEqual( |
| ([True] * 4, 1), |
| self.try_runner.step_db.revision_quality_builder_steps( |
| 'mac', 123)) |
| self.try_server.check_calls( |
| ['builders/?select=linux&select=mac', |
| 'builders/linux/builds/_all', 'builders/mac/builds/_all']) |
| self.context.status.check_names(['try server'] * 6) |
| |
| def testNames(self): |
| job = try_server.TryJob( |
| builder='builder', revision=123, tests=['test1'], clobber=False) |
| self.assertEqual(None, job.name) |
| |
| def testLostJob(self): |
| # Test that a job is automatically retried if it was never started up. It |
| # does happen. |
| self.try_runner.verify(self.pending) |
| self.try_server.check_calls( |
| ['trychange b={linux:test1,test2, mac:test1,test2} c=False r=sol@123']) |
| |
| # Keep a copy of the try jobs to compare later. |
| self.try_runner.update_status([self.pending]) |
| self.try_server.check_calls( |
| [ 'builders/?select=linux&select=mac', |
| 'builders/linux/builds/_all', 'builders/mac/builds/_all']) |
| self.assertPending( |
| base.PROCESSING, 2, None, mac_sent=self.timestamp[-1], |
| linux_build=None, mac_build=None) |
| |
| # lost_try_job_delay + 2 seconds later. |
| # linux is pending, mac is lost. |
| self.try_server.pending_builds['linux'] = [ |
| { |
| 'reason': '42-23', |
| } |
| ] |
| self.timestamp.append(self.try_runner.lost_try_job_delay + 2) |
| self.try_runner.update_status([self.pending]) |
| self.assertPending( |
| base.PROCESSING, 2, None, mac_sent=self.timestamp[-1], |
| linux_build=None, mac_build=None, |
| mac_name='42-23 (previous was lost)') |
| self.try_server.check_calls( |
| # Look if there is pending build on each builder. |
| [ 'builders/?select=linux&select=mac', |
| 'builders/linux/builds/_all', 'builders/mac/builds/_all', |
| 'builders/linux/pendingBuilds', |
| # Retry only mac. |
| 'trychange b={mac:test1,test2} c=False r=sol@123 n=42-23 (previous ' |
| 'was lost)']) |
| |
| # linux job was completed. |
| self.try_server.pending_builds['linux'] = [] |
| self.try_server.add_build( |
| 'linux', 123, None, [SUCCESS, SUCCESS, SUCCESS, SUCCESS]) |
| self.try_runner.update_status([self.pending]) |
| self.assertPending( |
| base.PROCESSING, 2, None, mac_sent=self.timestamp[1], |
| linux_build=0, mac_build=None, |
| linux_state=base.SUCCEEDED, |
| mac_name='42-23 (previous was lost)') |
| self.try_server.check_calls( |
| [ 'builders/?select=linux&select=mac', |
| 'builders/linux/builds/_all', 'builders/mac/builds/_all']) |
| |
| # 2 * (lost_try_job_delay + 2) seconds later, mac job started and completed. |
| self.timestamp.append(2 * (self.try_runner.lost_try_job_delay + 2)) |
| self.try_server.add_build( |
| 'mac', 123, '42-23 (previous was lost)', |
| [SUCCESS, SUCCESS, SUCCESS, SUCCESS]) |
| self.try_runner.update_status([self.pending]) |
| self.assertPending( |
| base.SUCCEEDED, 2, None, mac_sent=self.timestamp[1], |
| linux_build=0, mac_build=0, |
| mac_name='42-23 (previous was lost)') |
| self.try_server.check_calls( |
| ['builders/?select=mac', 'builders/mac/builds/_all']) |
| |
| self.try_runner.update_status([self.pending]) |
| self.context.checkout.check_calls( |
| [ 'prepare(123)', |
| 'apply_patch(%r)' % self.context.rietveld.patchsets[-1]]) |
| self.context.status.check_names(['try server'] * 7) |
| |
| def testFailedStepRetryLkgr(self): |
| self.try_runner.verify(self.pending) |
| self.try_server.check_calls( |
| ['trychange b={linux:test1,test2, mac:test1,test2} c=False r=sol@123']) |
| self.try_server.add_build( |
| 'linux', 123, None, [SUCCESS, SUCCESS, FAILURE, SUCCESS]) |
| |
| self.lkgr = 122 |
| self.try_runner.update_status([self.pending]) |
| self.try_server.check_calls( |
| [ 'builders/?select=linux&select=mac', |
| 'builders/linux/builds/_all', 'builders/mac/builds/_all', |
| # Only the failed test is retried, on lkgr revision. |
| 'trychange b={linux:test1} c=False r=sol@122 n=42-23 (retry)']) |
| self.assertEqual(['test1'], self.get_verif().try_jobs[0].failed_steps) |
| self.context.checkout.check_calls( |
| [ 'prepare(122)', |
| 'apply_patch(%r)' % self.context.rietveld.patchsets[-1]]) |
| self.context.status.check_names(['try server'] * 5) |
| |
| def testFailedUpdate(self): |
| # It must not retry a failed update. |
| # Add succeededing builds, this sets quality to True, which disable retry |
| # mechanism. |
| self.try_server.add_build( |
| 'linux', 123, 'georges tried stuff', |
| [SUCCESS, SUCCESS, SUCCESS, SUCCESS]) |
| self.try_server.add_build( |
| 'mac', 123, 'georges tried stuff', [SUCCESS, SUCCESS, SUCCESS, SUCCESS]) |
| self.lkgr = 123 |
| |
| self.try_runner.verify(self.pending) |
| self.try_server.check_calls( |
| ['trychange b={linux:test1,test2, mac:test1,test2} c=False r=sol@123']) |
| self.try_server.add_build( |
| 'linux', 123, None, [FAILURE, None, None, None]) |
| self.try_runner.update_status([self.pending]) |
| self.try_server.check_calls( |
| [ 'builders/?select=linux&select=mac', |
| 'builders/linux/builds/_all', 'builders/mac/builds/_all']) |
| self.assertEqual('linux', self.get_verif().try_jobs[0].builder) |
| self.assertEqual(['update'], self.get_verif().try_jobs[0].failed_steps) |
| self.assertPending( |
| base.FAILED, 2, |
| (u'Try job failure for 42-23 on linux for step ' |
| u'"update".\n%s/buildstatus?builder=linux&number=1\n\n' |
| u'Step "update" is always a major failure.\n' |
| u'Look at the try server FAQ for more details.') % |
| self.try_server.server_url, |
| mac_build=None, |
| mac_state=base.PROCESSING) |
| self.context.status.check_names(['try server'] * 4) |
| |
| def testFailedCompileRetryClobber(self): |
| # It must retry once a non-clobber compile. |
| self.try_runner.verify(self.pending) |
| self.try_server.check_calls( |
| ['trychange b={linux:test1,test2, mac:test1,test2} c=False r=sol@123']) |
| self.try_server.add_build( |
| 'linux', 123, None, [SUCCESS, FAILURE, None, None]) |
| self.lkgr = 122 |
| self.try_runner.update_status([self.pending]) |
| self.context.checkout.check_calls( |
| [ 'prepare(122)', |
| 'apply_patch(%r)' % self.context.rietveld.patchsets[-1]]) |
| self.try_server.check_calls( |
| [ 'builders/?select=linux&select=mac', |
| 'builders/linux/builds/_all', 'builders/mac/builds/_all', |
| # Retries at lkgr. |
| 'trychange b={linux:test1,test2} c=True r=sol@122 n=42-23 (retry)']) |
| self.assertEqual(['compile'], self.get_verif().try_jobs[0].failed_steps) |
| self.assertPending( |
| base.PROCESSING, 2, None, linux_rev=122, |
| linux_clobber=True, linux_build=None, mac_build=None, |
| linux_name='42-23 (retry)') |
| |
| self.try_server.add_build( |
| 'linux', 122, '42-23 (retry)', [SUCCESS, FAILURE, None, None]) |
| self.try_runner.update_status([self.pending]) |
| self.try_server.check_calls( |
| [ 'builders/?select=linux&select=mac', |
| 'builders/linux/builds/_all', 'builders/mac/builds/_all']) |
| self.assertEqual(['compile'], self.get_verif().try_jobs[0].failed_steps) |
| self.assertPending( |
| base.FAILED, 2, |
| self.second_fail_msg('42-23 (retry)', 'compile', 'compile', 'linux', 1, |
| True), |
| linux_rev=122, |
| linux_clobber=True, |
| mac_build=None, |
| mac_state=base.PROCESSING, |
| linux_name='42-23 (retry)') |
| self.context.status.check_names(['try server'] * 7) |
| |
| def testTooManyRetries(self): |
| self.try_runner.verify(self.pending) |
| self.try_server.check_calls( |
| ['trychange b={linux:test1,test2, mac:test1,test2} c=False r=sol@123']) |
| job = self.pending.verifications[self.try_runner.name].try_jobs[0] |
| self.try_runner._send_jobs( |
| self.pending, [job], False, {job.builder:job.tests}, 'foo') |
| self.try_server.check_calls( |
| [ 'trychange b={linux:test1,test2} c=False r=sol@123 n=foo']) |
| job = self.pending.verifications[self.try_runner.name].try_jobs[0] |
| self.try_runner._send_jobs( |
| self.pending, [job], False, {job.builder:job.tests}, 'foo') |
| self.try_server.check_calls( |
| [ 'trychange b={linux:test1,test2} c=False r=sol@123 n=foo']) |
| job = self.pending.verifications[self.try_runner.name].try_jobs[0] |
| self.try_runner._send_jobs( |
| self.pending, [job], False, {job.builder:job.tests}, 'foo') |
| self.try_server.check_calls( |
| [ 'trychange b={linux:test1,test2} c=False r=sol@123 n=foo']) |
| job = self.pending.verifications[self.try_runner.name].try_jobs[0] |
| try: |
| self.try_runner._send_jobs( |
| self.pending, [job], False, {job.builder:job.tests}, 'foo') |
| self.fail() |
| except base.DiscardPending: |
| pass |
| self.context.status.check_names(['try server'] * 5) |
| |
| def testNoTry(self): |
| self.pending.description += '\nNOTRY=true' |
| self.try_runner.verify(self.pending) |
| self.assertEqual( |
| base.SUCCEEDED, |
| self.pending.verifications[self.try_runner.name].get_state()) |
| |
| def testNoTryWrong(self): |
| self.pending.description += '\nNOTRY=true2' |
| self.try_runner.verify(self.pending) |
| self.try_server.check_calls( |
| ['trychange b={linux:test1,test2, mac:test1,test2} c=False r=sol@123']) |
| self.context.status.check_names(['try server'] * 2) |
| self.assertEqual( |
| base.PROCESSING, |
| self.pending.verifications[self.try_runner.name].get_state()) |
| |
| def testSuccessIgnoredFailure(self): |
| # Simplify testing code by removing mac. |
| del self.try_runner.builders_and_tests['mac'] |
| # Add fake failing ignored step. |
| self.try_server.steps = [ |
| 'update', 'compile', 'ignored_step', 'test1', 'test2'] |
| |
| self.try_runner.verify(self.pending) |
| self.try_server.check_calls( |
| ['trychange b={linux:test1,test2} c=False r=sol@123']) |
| |
| self.try_runner.update_status([self.pending]) |
| self.assertPending( |
| base.PROCESSING, 1, None, linux_build=None, mac_build=None) |
| self.try_server.check_calls( |
| ['builders/?select=linux', 'builders/linux/builds/_all']) |
| |
| self.try_server.add_build( |
| 'linux', 123, None, [SUCCESS, SUCCESS, FAILURE, SUCCESS, SUCCESS]) |
| self.try_runner.update_status([self.pending]) |
| self.assertPending(base.SUCCEEDED, 1, None, linux_build=0, mac_build=None) |
| self.try_server.check_calls( |
| ['builders/?select=linux', 'builders/linux/builds/_all']) |
| |
| self.try_server.set_build_result('linux', SUCCESS) |
| self.try_runner.update_status([self.pending]) |
| self.assertPending(base.SUCCEEDED, 1, None, linux_build=0, mac_build=0) |
| # TODO(maruel): Fix, since StepDb doesn't know about ignored steps. |
| self.assertEqual( |
| None, self.try_runner.step_db.last_good_revision_builder('linux')) |
| self.assertEqual( |
| ([True, True, False, True, True], 1), |
| self.try_runner.step_db.revision_quality_builder_steps( |
| 'linux', 123)) |
| self.context.status.check_names(['try server'] * 3) |
| |
| |
| if __name__ == '__main__': |
| logging.basicConfig( |
| level=[logging.WARNING, logging.INFO, logging.DEBUG][ |
| min(sys.argv.count('-v'), 2)], |
| format='%(levelname)5s %(module)15s(%(lineno)3d): %(message)s') |
| unittest.main() |