blob: 2cdeb4f7a6c113dc1f070b47a43c9d7fe5ad20b6 [file] [log] [blame]
#!/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_job_on_rietveld.py."""
import logging
import os
import random
import string
import sys
import time
import unittest
ROOT_DIR = os.path.dirname(os.path.abspath(__file__))
sys.path.insert(0, os.path.join(ROOT_DIR, '..'))
# In tests/
import mocks
from mocks import BuildbotBuildStep
# In root
from verification import base
from verification import try_job_steps
from verification import try_job_on_rietveld
# Some tests produce more output if verbose, so we need to
# track if we are in verbose mode or not.
VERBOSE = False
def _posted(builders):
return 'trigger_try_jobs(42, 23, \'CQ\', False, \'HEAD\', %s)' % str(builders)
def gen_job_pending(**kwargs):
value = {
'__persistent_type__': 'RietveldTryJobPending',
'builder': None,
'clobber': False,
'init_time': 1.,
'requested_steps': [],
'revision': None,
'tries': 1,
}
assert all(arg in value for arg in kwargs)
value.update(kwargs)
return value
def gen_job(**kwargs):
value = {
'__persistent_type__': 'RietveldTryJob',
'build': None,
'builder': None,
'clobber': False,
'completed': False,
'init_time': 1.,
'parent_key': None,
'requested_steps': [],
'revision': None,
'started': 1,
'steps_failed': [],
'steps_passed': [],
'tries': 1,
}
assert all(arg in value for arg in kwargs)
value.update(kwargs)
return value
def gen_jobs(**kwargs):
value = {
'__persistent_type__': 'RietveldTryJobs',
'error_message': None,
'irrelevant': [],
'pendings': [],
'skipped': False,
'step_verifiers': [],
'try_jobs': {},
}
for arg in kwargs:
if arg not in value:
raise Exception('Invalid arg %s' % str(arg))
value.update(kwargs)
# Convert all the verifiers to dicts (i.e. serialize them).
value['step_verifiers'] = [step.as_dict() for step in value['step_verifiers']]
return value
class TryJobOnRietveldBase(mocks.TestCase):
# Base class for all test cases testing try_job_rietveld.py.
def setUp(self):
super(TryJobOnRietveldBase, self).setUp()
self.timestamp = []
self.mock(time, 'time', self._time)
self.email = 'user1@example.com'
self.user = 'user1'
self.step_verifiers = [
try_job_steps.TryJobSteps(
builder_name='linux',
steps=['test1', 'test2']),
try_job_steps.TryJobSteps(
builder_name='mac',
steps=['test1', 'test2']),
]
def tearDown(self):
try:
if not self.has_failed():
self.assertEqual(0, len(self.timestamp))
finally:
super(TryJobOnRietveldBase, self).tearDown()
def _time(self):
self.assertTrue(self.timestamp)
return self.timestamp.pop(0)
class TryRunnerRietveldTest(TryJobOnRietveldBase):
# These test cases setup self.try_runner and call .verify() and
# .update_status() to test it.
def setUp(self):
super(TryRunnerRietveldTest, self).setUp()
self.try_runner = None
self._change_step_verifiers(self.step_verifiers)
# Patch it a little.
self.buildbot_status = mocks.BuildbotMock(self)
self.mock(
try_job_on_rietveld.buildbot_json,
'Buildbot',
lambda _: self.buildbot_status)
self.pending.revision = 123
# It's what rietveld is going to report.
self._key = (self.pending.issue, self.pending.patchset)
self.context.rietveld.patchsets_properties[self._key] = {
'try_job_results': [],
}
def tearDown(self):
try:
if not self.has_failed():
# Not to confuse with self.context.status which is AsyncPush mock.
self.buildbot_status.check_calls([])
finally:
super(TryRunnerRietveldTest, self).tearDown()
def _change_step_verifiers(self, step_verifiers):
"""Change the current step verifiers and update any objects that used
the old ones."""
self.step_verifiers = step_verifiers
# time is requested in the object construction below and self.timestamp
# will be empty at the end of this function call.
self.timestamp.append(1.)
self.try_runner = try_job_on_rietveld.TryRunnerRietveld(
self.context,
'http://foo/bar',
self.email,
self.step_verifiers,
['ignored_step'],
'sol',
)
self.try_runner.update_latency = 0
def _get_verif(self):
"""Returns the RietveldTryJobs instance associated to this PendCommit."""
return self.pending.verifications[self.try_runner.name]
def _assert_pending_is_empty(self):
actual = self._get_verif().as_dict()
expected = gen_jobs(
step_verifiers=[
try_job_steps.TryJobSteps(
builder_name='linux',
steps=['test1', 'test2']),
try_job_steps.TryJobSteps(
builder_name='mac',
steps=['test1', 'test2']),
],
pendings=[
gen_job_pending(builder=u'linux', requested_steps=['test1', 'test2']),
gen_job_pending(builder=u'mac', requested_steps=['test1', 'test2']),
])
self.assertEqual(expected, actual)
def _add_build(self, builder, buildnumber, revision, steps, completed):
"""Adds a build with a randomly generated key.
Adds the build to both the try server and to Rietveld.
"""
key = ''.join(random.choice(string.ascii_letters) for _ in xrange(8))
build = self.buildbot_status.add_build(
builder, buildnumber, revision, key, completed, None)
build.steps.extend(steps)
self.context.rietveld.patchsets_properties[self._key][
'try_job_results'].append(
{
'key': key,
'builder': builder,
'buildnumber': buildnumber,
})
return key
def _add_triggered_build(self, builder, buildnumber, revision, steps,
completed, parent_key):
"""Adds a triggered build with a randomly generated key.
Adds the build to both the try server and to Rietveld.
"""
key = ''.join(random.choice(string.ascii_letters) for _ in xrange(8))
build = self.buildbot_status.add_build(
builder, buildnumber, revision, key, completed, parent_key)
build.steps.extend(steps)
self.context.rietveld.patchsets_properties[self._key][
'try_job_results'].append(
{
'key': key,
'builder': builder,
'buildnumber': buildnumber,
})
return key
def check_pending(self, num_status, rietveld, state, err):
self.context.status.check_names(['try job rietveld'] * num_status)
self.context.rietveld.check_calls(rietveld)
self.assertEqual(state, self.pending.get_state())
self.assertEqual(err, self.pending.error_message())
def check_verif(self, need, waiting):
rietveld_try_jobs = self._get_verif()
self.assertEqual(need, rietveld_try_jobs.tests_need_to_be_run(1.))
self.assertEqual(waiting, rietveld_try_jobs.tests_waiting_for_result())
if not waiting:
why_not = None
else:
why_not = (
'Waiting for the following jobs:\n' +
''.join(
' %s: %s\n' % (b, ','.join(waiting[b])) for b in sorted(waiting)))
self.assertEqual(why_not, rietveld_try_jobs.why_not())
def call_verify(
self, timestamps, num_status, rietveld, state, err, need, waiting):
"""Calls TryRunnerRietveld.verify().
Makes sure the specified number of time.time() calls occurred.
"""
self.assertEqual(0, len(self.timestamp))
self.timestamp = timestamps
self.try_runner.verify(self.pending)
self.assertEqual(0, len(self.timestamp))
self.check_pending(num_status, rietveld, state, err)
self.check_verif(need, waiting)
def call_update_status(
self, timestamps, num_status, rietveld, state, err, need, waiting):
"""Calls TryRunnerRietveld.update_status().
"""
self.timestamp = timestamps
self.try_runner.update_status([self.pending])
self.check_pending(num_status, rietveld, state, err)
self.check_verif(need, waiting)
def testVoid(self):
self.assertEqual(self.pending.verifications.keys(), [])
self.assertEqual(base.PROCESSING, self.pending.get_state())
self.assertEqual('', self.pending.error_message())
def testVoidUpdate(self):
self.try_runner.update_status([])
self.assertEqual(base.PROCESSING, self.pending.get_state())
self.assertEqual('', self.pending.error_message())
def testVerificationVoid(self):
self.call_verify(
[1.] * 3,
num_status=2,
rietveld=[
_posted({u"linux": ["test1", "test2"]}),
_posted({u"mac": ["test1", "test2"]}),
],
state=base.PROCESSING,
err='',
need={},
waiting={'linux': ['test1', 'test2'], 'mac': ['test1', 'test2']})
self._assert_pending_is_empty()
def testVerificationUpdateNoJob(self):
self.call_verify(
[1.] * 3,
num_status=2,
rietveld=[
_posted({u"linux": ["test1", "test2"]}),
_posted({u"mac": ["test1", "test2"]}),
],
state=base.PROCESSING,
err='',
need={},
waiting={'linux': ['test1', 'test2'], 'mac': ['test1', 'test2']})
self._assert_pending_is_empty()
self.call_update_status(
[1.] * 2,
num_status=0,
rietveld=[],
state=base.PROCESSING,
err='',
need={},
waiting={'linux': ['test1', 'test2'], 'mac': ['test1', 'test2']})
self._assert_pending_is_empty()
def testVerificationUpdate(self):
self.call_verify(
[1.] * 3,
num_status=2,
rietveld=[
_posted({u"linux": ["test1", "test2"]}),
_posted({u"mac": ["test1", "test2"]}),
],
state=base.PROCESSING,
err='',
need={},
waiting={'linux': ['test1', 'test2'], 'mac': ['test1', 'test2']})
self._assert_pending_is_empty()
key = self._add_build('mac', 32, 42, [], False)
self.call_update_status(
[1.] * (3 + 1 * VERBOSE),
num_status=1,
rietveld=[],
state=base.PROCESSING,
err='',
need={},
waiting={'linux': ['test1', 'test2'], 'mac': ['test1', 'test2']})
expected = gen_jobs(
step_verifiers=[
try_job_steps.TryJobSteps(
builder_name='linux',
steps=['test1', 'test2']),
try_job_steps.TryJobSteps(
builder_name='mac',
steps=['test1', 'test2']),
],
pendings=[
gen_job_pending(builder='linux', requested_steps=['test1', 'test2']),
],
try_jobs={
key: gen_job(
builder='mac',
build=32,
requested_steps=['test1', 'test2'],
revision=42),
})
self.assertEqual(expected, self._get_verif().as_dict())
def testVerificationSuccess(self):
self.call_verify(
[1.] * 3,
num_status=2,
rietveld=[
_posted({u"linux": ["test1", "test2"]}),
_posted({u"mac": ["test1", "test2"]}),
],
state=base.PROCESSING,
err='',
need={},
waiting={'linux': ['test1', 'test2'], 'mac': ['test1', 'test2']})
key1 = self._add_build(
'mac', 32, 42,
[BuildbotBuildStep('test1', True), BuildbotBuildStep('test2', True)],
False)
key2 = self._add_build(
'linux', 32, 42,
[BuildbotBuildStep('test1', True), BuildbotBuildStep('test2', True)],
False)
self.call_update_status(
[1.] * (4 + 2 * VERBOSE),
num_status=2,
rietveld=[],
state=base.SUCCEEDED,
err='',
need={},
waiting={})
expected = gen_jobs(
step_verifiers=[
try_job_steps.TryJobSteps(
builder_name='linux',
steps=['test1', 'test2']),
try_job_steps.TryJobSteps(
builder_name='mac',
steps=['test1', 'test2']),
],
try_jobs={
key1: gen_job(
builder=u'mac',
build=32,
requested_steps=['test1', 'test2'],
steps_passed=['test1', 'test2'],
revision=42),
key2: gen_job(
builder=u'linux',
build=32,
requested_steps=['test1', 'test2'],
steps_passed=['test1', 'test2'],
revision=42),
})
self.assertEqual(expected, self._get_verif().as_dict())
def testVerificationRetrySuccess(self):
self.call_verify(
[1.] * 3,
num_status=2,
rietveld=[
_posted({u"linux": ["test1", "test2"]}),
_posted({u"mac": ["test1", "test2"]}),
],
state=base.PROCESSING,
err='',
need={},
waiting={'linux': ['test1', 'test2'], 'mac': ['test1', 'test2']})
key1 = self._add_build(
'mac', 32, 42,
[BuildbotBuildStep('test1', True), BuildbotBuildStep('test2', False)],
False)
key2 = self._add_build(
'linux', 32, 42,
[BuildbotBuildStep('test1', True), BuildbotBuildStep('test2', True)],
False)
self.call_update_status(
[1.] * (5 + 2 * VERBOSE),
num_status=3,
rietveld=[_posted({u"mac": ["test2"]})],
state=base.PROCESSING,
err='',
need={},
waiting={'mac': ['test2']})
expected = gen_jobs(
step_verifiers=[
try_job_steps.TryJobSteps(
builder_name='linux',
steps=['test1', 'test2']),
try_job_steps.TryJobSteps(
builder_name='mac',
steps=['test1', 'test2']),
],
pendings=[
gen_job_pending(builder=u'mac', requested_steps=['test2'], tries=2),
],
try_jobs={
key1: gen_job(
builder=u'mac',
build=32,
requested_steps=['test1', 'test2'],
steps_failed=['test2'],
steps_passed=['test1'],
revision=42),
key2: gen_job(
builder=u'linux',
build=32,
requested_steps=['test1', 'test2'],
steps_passed=['test1', 'test2'],
revision=42),
})
self.assertEqual(expected, self._get_verif().as_dict())
# Add a new build on mac where test2 passed.
key3 = self._add_build(
'mac', 33, 42,
[BuildbotBuildStep('test1', False), BuildbotBuildStep('test2', True)],
False)
self.call_update_status(
[1.] * (5 + 1 * VERBOSE),
num_status=3,
rietveld=[],
state=base.SUCCEEDED,
err='',
need={},
waiting={})
expected = gen_jobs(
step_verifiers=[
try_job_steps.TryJobSteps(
builder_name='linux',
steps=['test1', 'test2']),
try_job_steps.TryJobSteps(
builder_name='mac',
steps=['test1', 'test2']),
],
try_jobs={
key1: gen_job(
build=32,
builder='mac',
requested_steps=['test1', 'test2'],
steps_failed=['test2'],
steps_passed=['test1'],
revision=42),
key2: gen_job(
builder='linux',
build=32,
requested_steps=['test1', 'test2'],
revision=42,
steps_passed=['test1', 'test2']),
key3: gen_job(
builder='mac',
build=33,
requested_steps=['test2'],
revision=42,
steps_failed=['test1'],
steps_passed=['test2'],
tries=2),
})
self.assertEqual(expected, self._get_verif().as_dict())
def testVerificationRetryRetry(self):
self.call_verify(
[1.] * 3,
num_status=2,
rietveld=[
_posted({u"linux": ["test1", "test2"]}),
_posted({u"mac": ["test1", "test2"]}),
],
state=base.PROCESSING,
err='',
need={},
waiting={'linux': ['test1', 'test2'], 'mac': ['test1', 'test2']})
key1 = self._add_build(
'mac', 32, 42,
[BuildbotBuildStep('test1', True), BuildbotBuildStep('test2', False)],
False)
key2 = self._add_build(
'linux', 32, 42,
[BuildbotBuildStep('test1', True), BuildbotBuildStep('test2', True)],
False)
self.call_update_status(
[1.] * (5 + 2 * VERBOSE),
num_status=3,
rietveld=[_posted({u"mac": ["test2"]})],
state=base.PROCESSING,
err='',
need={},
waiting={'mac': ['test2']})
expected = gen_jobs(
step_verifiers=[
try_job_steps.TryJobSteps(
builder_name='linux',
steps=['test1', 'test2']),
try_job_steps.TryJobSteps(
builder_name='mac',
steps=['test1', 'test2']),
],
pendings=[
gen_job_pending(builder='mac', requested_steps=['test2'], tries=2),
],
try_jobs={
key1: gen_job(
builder='mac',
build=32,
requested_steps=['test1', 'test2'],
steps_failed=['test2'],
steps_passed=['test1'],
revision=42),
key2: gen_job(
builder='linux',
build=32,
requested_steps=['test1', 'test2'],
steps_passed=['test1', 'test2'],
revision=42),
})
self.assertEqual(expected, self._get_verif().as_dict())
# Add a new build on mac where test2 failed.
key3 = self._add_build('mac', 33, 42, [BuildbotBuildStep('test2', False)],
False)
self.call_update_status(
[1.] * (6 + 1 * VERBOSE),
num_status=4,
rietveld=[_posted({u"mac": ["test2"]})],
state=base.PROCESSING,
err='',
need={},
waiting={'mac': ['test2']})
expected = gen_jobs(
step_verifiers=[
try_job_steps.TryJobSteps(
builder_name='linux',
steps=['test1', 'test2']),
try_job_steps.TryJobSteps(
builder_name='mac',
steps=['test1', 'test2']),
],
pendings=[
gen_job_pending(builder='mac', requested_steps=['test2'], tries=3),
],
try_jobs={
key1: gen_job(
build=32,
builder='mac',
requested_steps=['test1', 'test2'],
steps_failed=['test2'],
steps_passed=['test1'],
revision=42),
key2: gen_job(
builder='linux',
build=32,
requested_steps=['test1', 'test2'],
revision=42,
steps_passed=['test1', 'test2']),
key3: gen_job(
builder='mac',
build=33,
requested_steps=['test2'],
revision=42,
steps_failed=['test2'],
tries=2),
})
self.assertEqual(expected, self._get_verif().as_dict())
# Add a new build on mac where test2 failed again! Too bad now.
self._add_build('mac', 34, 42, [BuildbotBuildStep('test2', False)], False)
self.call_update_status(
[1.] * (6 + 2 * VERBOSE),
num_status=4,
rietveld=[],
state=base.FAILED,
err=(
u'Retried try job too often on mac for step(s) test2\n'
'http://foo/bar/buildstatus?builder=mac&number=34'),
need={},
waiting={})
def testVerificationRetryRetryTriggered(self):
step_verifiers = [
try_job_steps.TryJobTriggeredSteps(
builder_name='triggered',
trigger_name='builder',
steps={'test1': 'build'})]
self._change_step_verifiers(step_verifiers)
self.call_verify(
[1.] * 3,
num_status=2,
rietveld=[
_posted({u"builder": ["build"]}),
],
state=base.PROCESSING,
err='',
need={},
waiting={'triggered': ['test1']})
# The trigger bot passed, but the triggered bot fails.
key1 = self._add_build('builder', 1, 42, [BuildbotBuildStep('build', True)],
True)
self._add_triggered_build('triggered', 1, 42,
[BuildbotBuildStep('test1', False)], True, key1)
triggered_key_1 = 'triggered/1_triggered_%s' % key1
self.call_update_status(
[1.] * (6 + 2 * VERBOSE),
num_status=4,
rietveld=[_posted({u"builder": ["build"]})],
state=base.PROCESSING,
err='',
need={},
waiting={'triggered': ['test1']})
expected = gen_jobs(
step_verifiers=[
try_job_steps.TryJobTriggeredSteps(
builder_name='triggered',
trigger_name='builder',
steps={'test1': 'build'})],
pendings=[
gen_job_pending(builder='builder', requested_steps=['build'], tries=2),
gen_job_pending(builder='triggered', requested_steps=['test1'],
tries=2),
],
try_jobs={
key1: gen_job(
builder='builder',
build=1,
requested_steps=['build'],
steps_passed=['build'],
completed=True,
revision=42,
tries=1,
),
triggered_key_1: gen_job(
builder='triggered',
build=1,
requested_steps=['test1'],
steps_failed=['test1'],
completed=True,
revision=42,
parent_key=key1,
tries=1,
),
})
self.assertEqual(expected, self._get_verif().as_dict())
# Triggered bot fails again.
key2 = self._add_build('builder', 2, 42, [BuildbotBuildStep('build', True)],
True)
self._add_triggered_build('triggered', 2, 42,
[BuildbotBuildStep('test1', False)], True, key2)
triggered_key_2 = 'triggered/2_triggered_%s' % key2
self.call_update_status(
[1.] * (6 + 2 * VERBOSE),
num_status=4,
rietveld=[_posted({u"builder": ["build"]})],
state=base.PROCESSING,
err='',
need={},
waiting={'triggered': ['test1']})
expected = gen_jobs(
step_verifiers=[
try_job_steps.TryJobTriggeredSteps(
builder_name='triggered',
trigger_name='builder',
steps={'test1': 'build'})],
pendings=[
gen_job_pending(builder='builder', requested_steps=['build'], tries=3),
gen_job_pending(builder='triggered', requested_steps=['test1'],
tries=3),
],
try_jobs={
key1: gen_job(
builder='builder',
build=1,
requested_steps=['build'],
steps_passed=['build'],
completed=True,
revision=42,
tries=1,
),
triggered_key_1: gen_job(
builder='triggered',
build=1,
requested_steps=['test1'],
steps_failed=['test1'],
completed=True,
revision=42,
parent_key=key1,
tries=1,
),
key2: gen_job(
builder='builder',
build=2,
requested_steps=['build'],
steps_passed=['build'],
completed=True,
revision=42,
tries=2,
),
triggered_key_2: gen_job(
builder='triggered',
build=2,
requested_steps=['test1'],
steps_failed=['test1'],
completed=True,
revision=42,
parent_key=key2,
tries=2,
),
})
self.assertEqual(expected, self._get_verif().as_dict())
# Triggered bot fails for the 3rd time, abort.
key3 = self._add_build('builder', 3, 1, [BuildbotBuildStep('build', True)],
True)
self._add_triggered_build('triggered', 3, 1,
[BuildbotBuildStep('test1', False)], True, key3)
self.call_update_status(
[1.] * (4 + 3 * VERBOSE),
num_status=2,
rietveld=[],
state=base.FAILED,
# TODO(csharp): Its the triggered bot that keeps failing, so that should
# be the bot mentioned here.
err=(
u'Retried try job too often on builder for step(s) build\n'
'http://foo/bar/buildstatus?builder=builder&number=3'),
need={},
waiting={})
def testVerificationPreviousJobGood(self):
# Reuse the previous job if good.
key1 = self._add_build(
'mac', 32, 42,
[BuildbotBuildStep('test1', True), BuildbotBuildStep('test2', True)],
False)
self.call_verify(
[1.] * (2 + 1 * VERBOSE),
num_status=1,
rietveld=[_posted({u"linux": ["test1", "test2"]})],
state=base.PROCESSING,
err='',
need={},
waiting={'linux': ['test1', 'test2']})
# Sends an update to note the job was started.
self.call_update_status(
[1.] * 3,
num_status=1,
rietveld=[],
state=base.PROCESSING,
err='',
need={},
waiting={'linux': ['test1', 'test2']})
expected = gen_jobs(
step_verifiers=[
try_job_steps.TryJobSteps(
builder_name='linux',
steps=['test1', 'test2']),
try_job_steps.TryJobSteps(
builder_name='mac',
steps=['test1', 'test2']),
],
pendings=[
gen_job_pending(builder='linux', requested_steps=['test1', 'test2']),
],
try_jobs={
key1: gen_job(
build=32,
builder='mac',
# Note that requested_steps is empty since testfilter is not parsed.
steps_passed=['test1', 'test2'],
revision=42,
# tries == 0 since we didn't start it.
tries=0),
})
self.assertEqual(expected, self._get_verif().as_dict())
def _expired(self, now, **kwargs):
# Exacly like testVerificationPreviousJobGood except that jobs are always
# too old, either by revision or by timestamp.
key1 = self._add_build(
'mac', 32, 42,
[BuildbotBuildStep('test1', True), BuildbotBuildStep('test2', True)],
False)
self.call_verify(
[now] * 3,
num_status=2,
rietveld=[
_posted({u"linux": ["test1", "test2"]}),
_posted({u"mac": ["test1", "test2"]}),
],
state=base.PROCESSING,
err='',
need={},
waiting={'linux': ['test1', 'test2'], 'mac': ['test1', 'test2']})
self.call_update_status(
[now] * 2,
num_status=0,
rietveld=[],
state=base.PROCESSING,
err='',
need={},
waiting={'linux': ['test1', 'test2'], 'mac': ['test1', 'test2']})
expected = gen_jobs(
step_verifiers=[
try_job_steps.TryJobSteps(
builder_name='linux',
steps=['test1', 'test2']),
try_job_steps.TryJobSteps(
builder_name='mac',
steps=['test1', 'test2']),
],
irrelevant=[key1],
pendings=[
gen_job_pending(
builder='linux', requested_steps=['test1', 'test2'], **kwargs),
gen_job_pending(
builder='mac', requested_steps=['test1', 'test2'], **kwargs),
])
self.assertEqual(expected, self._get_verif().as_dict())
def testVerificationPreviousExpiredRevisionTooOld(self):
self.context.checkout.revisions = lambda _r1, _r2: 201
self._expired(1.)
def testVerificationPreviousExpiredDateTooOld(self):
# 5 days old.
old = 5*24*60*60.
self._expired(old, init_time=old)
def _previous_job_partially_good(
self, steps, steps_failed, completed, expect_mac_retry, num_time_verify,
num_time_update, num_status_update):
# Reuse the previous job tests that passed.
key1 = self._add_build('mac', 32, 42, steps, completed)
expected_calls = [_posted({u"linux": ["test1", "test2"]})]
pendings = [
gen_job_pending(builder='linux', requested_steps=['test1', 'test2'])
]
if expect_mac_retry:
# No need to run test2 again.
expected_calls.append(_posted({u"mac": ["test1"]}))
pendings.append(gen_job_pending(builder='mac', requested_steps=['test1']))
self.call_verify(
[1.] * (num_time_verify + 1 * VERBOSE),
num_status=len(expected_calls),
rietveld=expected_calls,
state=base.PROCESSING,
err='',
need={},
waiting={'linux': ['test1', 'test2'], 'mac': ['test1']})
self.call_update_status(
[1.] * num_time_update,
num_status=num_status_update,
rietveld=[],
state=base.PROCESSING,
err='',
need={},
waiting={'linux': ['test1', 'test2'], 'mac': ['test1']})
expected = gen_jobs(
step_verifiers=[
try_job_steps.TryJobSteps(
builder_name='linux',
steps=['test1', 'test2']),
try_job_steps.TryJobSteps(
builder_name='mac',
steps=['test1', 'test2']),
],
pendings=pendings,
try_jobs={
key1: gen_job(
build=32,
builder='mac',
# Note that requested_steps is empty since testfilter is not parsed.
steps_failed=steps_failed,
steps_passed=['test2'],
revision=42,
# tries == 0 since we didn't start it.
tries=0,
completed=completed),
})
self.assertEqual(expected, self._get_verif().as_dict())
def testVerificationPreviousJobPartiallyGood1(self):
# Only test1 will be run on mac since test2 had passed.
self._previous_job_partially_good(
[BuildbotBuildStep('test1', False), BuildbotBuildStep('test2', True)],
['test1'], True, True, 3, 2, 0)
def testVerificationPreviousJobPartiallyGood2(self):
# Let's assume a testfilter was used and test1 wasn't run. Only test1 will
# be run on mac.
self._previous_job_partially_good(
[BuildbotBuildStep('test2', True)], [], True, True, 3, 2, 0)
def testVerificationPreviousJobPartiallyGood3(self):
# Test that we do not retry on mac until it completes. This is because
# CQ does not parse the test filter, so we do not know if the mac job
# will run test1.
# It's kind of weird, because it sends a status report like if it had
# started this job.
self._previous_job_partially_good(
[BuildbotBuildStep('test2', True)], [], False, False, 2, 3, 1)
def testVerificationPreviousJobsWereGood(self):
# Reuse the previous jobs tests that passed. Do not send any try job.
key1 = self._add_build(
'mac', 32, 42,
[BuildbotBuildStep('test1', True), BuildbotBuildStep('test2', True)],
False)
key2 = self._add_build(
'linux', 32, 42,
[BuildbotBuildStep('test1', True), BuildbotBuildStep('test2', True)],
False)
# People will love that!
self.call_verify(
[1.] * (1 + 2 * VERBOSE),
num_status=0,
rietveld=[],
state=base.SUCCEEDED,
err='',
need={},
waiting={})
self.call_update_status(
[1.],
num_status=0,
rietveld=[],
state=base.SUCCEEDED,
err='',
need={},
waiting={})
expected = gen_jobs(
step_verifiers=[
try_job_steps.TryJobSteps(
builder_name='linux',
steps=['test1', 'test2']),
try_job_steps.TryJobSteps(
builder_name='mac',
steps=['test1', 'test2']),
],
try_jobs={
key1: gen_job(
build=32,
builder='mac',
# Note that requested_steps is empty since testfilter is not parsed.
steps_passed=['test1', 'test2'],
revision=42,
# tries == 0 since we didn't start it.
tries=0),
key2: gen_job(
build=32,
builder='linux',
# Note that requested_steps is empty since testfilter is not parsed.
steps_passed=['test1', 'test2'],
revision=42,
# tries == 0 since we didn't start it.
tries=0),
})
self.assertEqual(expected, self._get_verif().as_dict())
def testRietveldTryJobsPendingWasLost(self):
# Requested a pending try job but the request was lost.
self.try_runner.step_verifiers = [
try_job_steps.TryJobSteps(
builder_name='linux',
steps=['test1']),
]
self.call_verify(
[1.] * 2,
num_status=1,
rietveld=[_posted({u"linux": ["test1"]})],
state=base.PROCESSING,
err='',
need={},
waiting={'linux': ['test1']})
self.call_update_status(
[1.] * 2,
num_status=0,
rietveld=[],
state=base.PROCESSING,
err='',
need={},
waiting={'linux': ['test1']})
# 3 minutes later
later = 3. * 60
self.call_update_status(
[later] * 2,
num_status=0,
rietveld=[],
state=base.PROCESSING,
err='',
need={},
waiting={'linux': ['test1']})
expected = gen_jobs(
step_verifiers=[
try_job_steps.TryJobSteps(
builder_name='linux',
steps=['test1']),
],
pendings=[
gen_job_pending(builder=u'linux', requested_steps=['test1']),
])
# 1h later.
later = 60. * 60
self.call_update_status(
[later] * (2 + 1 * VERBOSE),
num_status=0,
rietveld=[],
state=base.PROCESSING,
err='',
need={},
waiting={'linux': ['test1']})
expected = gen_jobs(
step_verifiers=[
try_job_steps.TryJobSteps(
builder_name='linux',
steps=['test1']),
],
pendings=[
gen_job_pending(builder=u'linux', requested_steps=['test1']),
])
self.assertEqual(expected, self._get_verif().as_dict())
def testRietveldTryJobsPendingTookSomeTime(self):
# Requested a pending try job but the request took some time to propagate.
self.try_runner.step_verifiers = [
try_job_steps.TryJobSteps(
builder_name='linux',
steps=['test1']),
]
self.call_verify(
[1.] * 2,
num_status=1,
rietveld=[_posted({u"linux": ["test1"]})],
state=base.PROCESSING,
err='',
need={},
waiting={'linux': ['test1']})
self.call_update_status(
[1.] * 2,
num_status=0,
rietveld=[],
state=base.PROCESSING,
err='',
need={},
waiting={'linux': ['test1']})
# 3 minutes later
later = 3. * 60
self.call_update_status(
[later] * 2,
num_status=0,
rietveld=[],
state=base.PROCESSING,
err='',
need={},
waiting={'linux': ['test1']})
expected = gen_jobs(
step_verifiers=[
try_job_steps.TryJobSteps(
builder_name='linux',
steps=['test1']),
],
pendings=[
gen_job_pending(builder=u'linux', requested_steps=['test1']),
])
self.assertEqual(expected, self._get_verif().as_dict())
# Queue it.
self.buildbot_status.builders['linux'].pending_builds.data = [
{
"builderName":"linux",
"builds":[],
"reason":"%d-1: None" % self.pending.issue,
"source": {
"changes": [
{
"at": "Wed 05 Dec 2012 19:11:19",
"files": [],
"number": 268857,
"project": "",
"properties": [],
"rev": "171358",
"revision": "171358",
"when": 1354763479,
"who": self.pending.owner,
},
],
"hasPatch": False,
"project": "chrome",
"repository": "",
"revision": "171358",
},
"submittedAt": 1354763479,
},
]
# 1h later, it must not have queued another job.
later = 60. * 60
self.call_update_status(
[later] * 2,
num_status=0,
rietveld=[],
state=base.PROCESSING,
err='',
need={},
waiting={'linux': ['test1']})
expected = gen_jobs(
step_verifiers=[
try_job_steps.TryJobSteps(
builder_name='linux',
steps=['test1']),
],
pendings=[
gen_job_pending(
builder=u'linux', requested_steps=['test1']),
])
self.assertEqual(expected, self._get_verif().as_dict())
def testRietveldHung(self):
# Send a try job and have it never start.
self.try_runner.step_verifiers = [
try_job_steps.TryJobSteps(
builder_name='linux',
steps=['test1']),
]
self.call_verify(
[1.] * 2,
num_status=1,
rietveld=[_posted({u"linux": ["test1"]})],
state=base.PROCESSING,
err='',
need={},
waiting={'linux': ['test1']})
key1 = self._add_build(
'linux', 32, 42,
[],
False)
self.call_update_status(
[1.] * (3 + 1 * VERBOSE),
num_status=1,
rietveld=[],
state=base.PROCESSING,
err='',
need={},
waiting={'linux': ['test1']})
expected = gen_jobs(
step_verifiers=[
try_job_steps.TryJobSteps(
builder_name='linux',
steps=['test1']),
],
try_jobs={
key1: gen_job(
builder=u'linux',
build=32,
requested_steps=['test1'],
steps_passed=[],
revision=42,
completed=False),
})
self.assertEqual(expected, self._get_verif().as_dict())
# Fast forward in time to make the try job request "timeout". Basically,
# the Try Job trigger on Rietveld was somehow ignored by the Try Server
# itself. Test our chance by sending another trigger.
later = 40. * 24 * 60 * 60
# Update the internal status.
self.call_update_status(
[later] * 3,
num_status=1,
rietveld=[_posted({u"linux": ["test1"]})],
state=base.PROCESSING,
err='',
need={},
waiting={'linux': ['test1']})
self.assertEqual(
'Waiting for the following jobs:\n linux: test1\n',
self._get_verif().why_not())
expected_timed_out = gen_jobs(
step_verifiers=[
try_job_steps.TryJobSteps(
builder_name='linux',
steps=['test1']),
],
irrelevant=[key1],
pendings=[
gen_job_pending(
builder=u'linux', init_time=later, requested_steps=['test1']),
])
self.assertEqual(expected_timed_out, self._get_verif().as_dict())
def testVerificationBrokenTestOnHead(self):
# The CQ retries until it aborts because the test is broken at HEAD on
# mac/test2.
self.call_verify(
[1.] * 3,
num_status=2,
rietveld=[
_posted({u"linux": ["test1", "test2"]}),
_posted({u"mac": ["test1", "test2"]}),
],
state=base.PROCESSING,
err='',
need={},
waiting={'linux': ['test1', 'test2'], 'mac': ['test1', 'test2']})
key1 = self._add_build(
'mac', 32, 42,
[BuildbotBuildStep('test1', True), BuildbotBuildStep('test2', False)],
True)
key2 = self._add_build(
'linux', 12, 22,
[BuildbotBuildStep('test1', True), BuildbotBuildStep('test2', True)],
True)
self.call_update_status(
[1.] * (5 + 2 * VERBOSE),
num_status=3,
rietveld=[_posted({u"mac": ["test2"]})],
state=base.PROCESSING,
err='',
need={},
waiting={'mac': ['test2']})
expected = gen_jobs(
step_verifiers=[
try_job_steps.TryJobSteps(
builder_name='linux',
steps=['test1', 'test2']),
try_job_steps.TryJobSteps(
builder_name='mac',
steps=['test1', 'test2']),
],
pendings=[
gen_job_pending(builder=u'mac', requested_steps=['test2'], tries=2),
],
try_jobs={
key1: gen_job(
builder=u'mac',
build=32,
completed=True,
requested_steps=['test1', 'test2'],
steps_failed=['test2'],
steps_passed=['test1'],
revision=42),
key2: gen_job(
builder=u'linux',
build=12,
completed=True,
requested_steps=['test1', 'test2'],
steps_passed=['test1', 'test2'],
revision=22),
})
self.assertEqual(expected, self._get_verif().as_dict())
key3 = self._add_build(
'mac', 33, 42,
[BuildbotBuildStep('test2', False)],
True)
self.call_update_status(
[1.] * (4 + 1 * VERBOSE),
num_status=2,
rietveld=[_posted({u"mac": ["test2"]})],
state=base.PROCESSING,
err='',
need={},
waiting={'mac': ['test2']})
expected = gen_jobs(
step_verifiers=[
try_job_steps.TryJobSteps(
builder_name='linux',
steps=['test1', 'test2']),
try_job_steps.TryJobSteps(
builder_name='mac',
steps=['test1', 'test2']),
],
pendings=[
gen_job_pending(builder=u'mac', requested_steps=['test2'], tries=3),
],
try_jobs={
key1: gen_job(
build=32,
builder=u'mac',
completed=True,
requested_steps=['test1', 'test2'],
steps_failed=['test2'],
steps_passed=['test1'],
revision=42),
key2: gen_job(
builder=u'linux',
build=12,
completed=True,
requested_steps=['test1', 'test2'],
revision=22,
steps_passed=['test1', 'test2']),
key3: gen_job(
builder=u'mac',
completed=True,
build=33,
requested_steps=['test2'],
revision=42,
steps_failed=['test2'],
tries=2),
})
self.assertEqual(expected, self._get_verif().as_dict())
self.call_update_status(
[1.] * (2 + 1 * VERBOSE),
num_status=0,
rietveld=[],
state=base.PROCESSING,
err='',
need={},
waiting={'mac': ['test2']})
class RietveldTryJobsTest(TryJobOnRietveldBase):
# Directly test RietveldTryJobs without constructing a TryRunnerRietveld.
# They should never touch self.timestamp.
def testInitial(self):
now = 1.
jobs = try_job_on_rietveld.RietveldTryJobs()
jobs.step_verifiers = [
try_job_steps.TryJobSteps(
builder_name='builder1',
steps=['test10', 'test11']),
]
jobs.try_jobs['key1'] = try_job_on_rietveld.RietveldTryJob(
init_time=now,
builder='builder1',
build=12,
revision=13,
requested_steps=['test10'],
started=int(now),
steps_passed=['test10'],
steps_failed=[],
clobber=False,
completed=True,
tries=1,
parent_key=None)
self.assertEqual({'builder1': ['test11']}, jobs.tests_need_to_be_run(now))
self.assertEqual({'builder1': ['test11']}, jobs.tests_waiting_for_result())
def testPending(self):
now = 1.
jobs = try_job_on_rietveld.RietveldTryJobs()
jobs.step_verifiers = [
try_job_steps.TryJobSteps(
builder_name='builder1',
steps=['test10', 'test11']),
]
jobs.try_jobs['key1'] = try_job_on_rietveld.RietveldTryJob(
init_time=now,
builder='builder1',
build=12,
revision=13,
requested_steps=['test10'],
started=int(now),
steps_passed=['test10'],
steps_failed=[],
clobber=False,
completed=True,
tries=1,
parent_key=None)
jobs.pendings.append(
try_job_on_rietveld.RietveldTryJobPending(
init_time=now,
builder='builder1',
revision=13,
requested_steps=['test11'],
clobber=False,
tries=1))
self.assertEqual({}, jobs.tests_need_to_be_run(now))
self.assertEqual({'builder1': ['test11']}, jobs.tests_waiting_for_result())
def testTriggeredPending(self):
# Construct an instance that has both tests to trigger and tests that are
# pending results.
now = 1.
jobs = try_job_on_rietveld.RietveldTryJobs()
jobs.step_verifiers = [
try_job_steps.TryJobSteps(
builder_name='builder1',
steps=['test10', 'test11']),
try_job_steps.TryJobSteps(
builder_name='builder2',
steps=['test20', 'test21']),
]
jobs.try_jobs['key1'] = try_job_on_rietveld.RietveldTryJob(
init_time=now,
builder='builder1',
build=12,
revision=13,
requested_steps=['test10'],
started=int(now),
steps_passed=['test10'],
steps_failed=[],
clobber=False,
completed=True,
tries=1,
parent_key=None)
jobs.try_jobs['key2'] = try_job_on_rietveld.RietveldTryJob(
init_time=now,
builder='builder2',
build=13,
revision=14,
requested_steps=[],
started=int(now),
steps_passed=['test21'],
steps_failed=[],
clobber=False,
completed=False,
tries=1,
parent_key=None)
jobs.pendings.append(
try_job_on_rietveld.RietveldTryJobPending(
init_time=now,
builder='builder2',
revision=14,
requested_steps=['test20'],
clobber=False,
tries=1))
# test11 is still not queued to be run but build with test20 in it has still
# not started yet.
self.assertEqual({'builder1': ['test11']}, jobs.tests_need_to_be_run(now))
self.assertEqual(
{'builder1': ['test11'], 'builder2': ['test20']},
jobs.tests_waiting_for_result())
def testAddTriggeredBot(self):
jobs = try_job_on_rietveld.RietveldTryJobs()
jobs.step_verifiers = [try_job_steps.TryJobTriggeredSteps(
builder_name='tester1',
trigger_name='builder1',
steps={'test3': 'trigger'})]
self.assertEqual({'builder1': ['trigger']}, jobs.tests_need_to_be_run(1.))
self.assertEqual({'tester1': ['test3']}, jobs.tests_waiting_for_result())
def testGenerateWatchedBuilders(self):
jobs = try_job_on_rietveld.RietveldTryJobs()
jobs.step_verifiers = [
try_job_steps.TryJobSteps(
builder_name='builder1',
steps=['test']),
try_job_steps.TryJobTriggeredSteps(
builder_name='triggered1',
trigger_name='trigger_bot1',
steps={'test': 'test'}),
try_job_steps.TryJobTriggeredOrNormalSteps(
builder_name='triggered2',
trigger_name='trigger_bot2',
trigger_bot_steps=['test'],
steps={'test': 'test'},
use_triggered_bot=False)
]
expected_watched_builders = [
'builder1',
'trigger_bot1',
'trigger_bot2',
'triggered1',
'triggered2'
]
self.assertEqual(sorted(expected_watched_builders),
sorted(jobs.watched_builders()))
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')
if '-v' in sys.argv:
VERBOSE = True
unittest.TestCase.maxDiff = None
unittest.main()