Skip to content

Commit

Permalink
update all tests to work with the new schedulerdb
Browse files Browse the repository at this point in the history
Big patch. Much of the work is to create a master (with a db) before
starting the test. It is now mandatory to wait for master.loadConfig's
Deferred. Master shutdown must be waited on too. And some of the
BuildRequest submission paths changed.

This also fixes a few pyflakes warnings.
  • Loading branch information
warner committed Feb 15, 2010
1 parent 10c01a7 commit ff021f6
Show file tree
Hide file tree
Showing 31 changed files with 2,980 additions and 2,311 deletions.
88 changes: 88 additions & 0 deletions buildbot/test/pollmixin.py
@@ -0,0 +1,88 @@
# ***** BEGIN LICENSE BLOCK *****
# Version: MPL 1.1/GPL 2.0/LGPL 2.1
#
# The contents of this file are subject to the Mozilla Public License Version
# 1.1 (the "License"); you may not use this file except in compliance with
# the License. You may obtain a copy of the License at
# http://www.mozilla.org/MPL/
#
# Software distributed under the License is distributed on an "AS IS" basis,
# WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
# for the specific language governing rights and limitations under the
# License.
#
# The Original Code is Mozilla-specific Buildbot steps.
#
# The Initial Developer of the Original Code is
# Mozilla Foundation.
# Portions created by the Initial Developer are Copyright (C) 2009
# the Initial Developer. All Rights Reserved.
#
# Contributor(s):
# Brian Warner <warner@lothar.com>
#
# Alternatively, the contents of this file may be used under the terms of
# either the GNU General Public License Version 2 or later (the "GPL"), or
# the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
# in which case the provisions of the GPL or the LGPL are applicable instead
# of those above. If you wish to allow use of your version of this file only
# under the terms of either the GPL or the LGPL, and not to allow others to
# use your version of this file under the terms of the MPL, indicate your
# decision by deleting the provisions above and replace them with the notice
# and other provisions required by the GPL or the LGPL. If you do not delete
# the provisions above, a recipient may use your version of this file under
# the terms of any one of the MPL, the GPL or the LGPL.
#
# ***** END LICENSE BLOCK *****

# copied from tahoe. I'm the original author. -Brian

import time
from twisted.internet import task

class TimeoutError(Exception):
pass

class PollComplete(Exception):
pass

class PollMixin:
_poll_should_ignore_these_errors = []

def poll(self, check_f, pollinterval=0.01, timeout=1000):
# Return a Deferred, then call check_f periodically until it returns
# True, at which point the Deferred will fire.. If check_f raises an
# exception, the Deferred will errback. If the check_f does not
# indicate success within timeout= seconds, the Deferred will
# errback. If timeout=None, no timeout will be enforced, and the loop
# will poll forever (or really until Trial times out).
cutoff = None
if timeout is not None:
cutoff = time.time() + timeout
lc = task.LoopingCall(self._poll, check_f, cutoff)
d = lc.start(pollinterval)
def _convert_done(f):
f.trap(PollComplete)
return None
d.addErrback(_convert_done)
return d

def _poll(self, check_f, cutoff):
if cutoff is not None and time.time() > cutoff:
raise TimeoutError("PollMixin never saw %s return True" % check_f)
if check_f():
raise PollComplete()
# since PollMixin is mostly used for unit tests, quit if we see any
# logged errors. This should give us a nice fast failure, instead of
# waiting for a timeout. Tests which use flushLoggedErrors() will
# need to warn us by putting the error types they'll be ignoring in
# self._poll_should_ignore_these_errors
if hasattr(self, "_observer") and hasattr(self._observer, "getErrors"):
errs = []
for e in self._observer.getErrors():
if not e.check(*self._poll_should_ignore_these_errors):
errs.append(e)
if errs:
print errs
self.fail("Errors snooped, terminating early")

17 changes: 9 additions & 8 deletions buildbot/test/runs/test_console.py
Expand Up @@ -115,21 +115,22 @@ def childLink(self, link):

# Class to test the method getBuildDetails in ConsoleStatusResource.
class GetBuildDetailsTests(RunMixin, unittest.TestCase):
# Test ConsoleStatusResource.getBuildDetails with a success and a failure case.
# Test ConsoleStatusResource.getBuildDetails with a success and a failure
# case.
def testgetBuildDetails(self):
# run an actual build with a step that will succeed, then another build with
# a step that will fail, then make sure the build details generated contains
# the right data.
# run an actual build with a step that will succeed, then another
# build with a step that will fail, then make sure the build details
# generated contains the right data.
d = self.master.loadConfig(run_config)
d.addCallback(lambda res: self.master.startService())
d.addCallback(lambda res: self.connectOneSlave("bot1"))
d.addCallback(lambda res: self.requestBuild("full1"))

# Make sure the build details returned is an empty string to signify that
# everything was ok
# Make sure the build details returned is an empty string to signify
# that everything was ok
def expectSuccess(bs):
console_status = console.ConsoleStatusResource()
results = console_status.getBuildDetails(MockRequest(), "buildername", bs);
results = console_status.getBuildDetails(MockRequest(),
"buildername", bs);
self.assertEqual(results, {})
d.addCallback(expectSuccess)

Expand Down
111 changes: 59 additions & 52 deletions buildbot/test/runs/test_dependencies.py
Expand Up @@ -2,10 +2,9 @@

from twisted.trial import unittest

from twisted.internet import reactor, defer

from buildbot.test.runutils import RunMixin
from buildbot.test.runutils import MasterMixin, StallMixin
from buildbot.status import base
from buildbot.changes.changes import Change

config_1 = """
from buildbot import scheduler
Expand All @@ -26,9 +25,16 @@
# -> downstream4 (b3, b4)
# -> downstream5 (b5)
s1 = scheduler.Scheduler('upstream1', None, 10, ['slowpass', 'fastfail'])
def fimp1(c):
return bool(c.files)
def fimp2(c):
return not fimp1(c)
s1 = scheduler.Scheduler('upstream1', None, None, ['slowpass', 'fastfail'],
fileIsImportant=fimp1)
s2 = scheduler.Dependent('downstream2', s1, ['b3', 'b4'])
s3 = scheduler.Scheduler('upstream3', None, 10, ['fastpass', 'slowpass'])
s3 = scheduler.Scheduler('upstream3', None, None, ['fastpass', 'slowpass'],
fileIsImportant=fimp2)
s4 = scheduler.Dependent('downstream4', s3, ['b3', 'b4'])
s5 = scheduler.Dependent('downstream5', s4, ['b5'])
c['schedulers'] = [s1, s2, s3, s4, s5]
Expand Down Expand Up @@ -62,41 +68,38 @@ def __init__(self, master):
def buildStarted(self, builderName, build):
self.builds.append(builderName)

class Dependencies(RunMixin, unittest.TestCase):
def setUp(self):
RunMixin.setUp(self)
self.master.loadConfig(config_1)
self.master.startService()
d = self.connectSlave(["slowpass", "fastfail", "fastpass",
"b3", "b4", "b5"])
return d

def findScheduler(self, name):
for s in self.master.allSchedulers():
if s.name == name:
return s
raise KeyError("No Scheduler named '%s'" % name)
class Dependencies(MasterMixin, StallMixin, unittest.TestCase):

def testParse(self):
self.master.loadConfig(config_1)
self.basedir = "dependencies/Dependencies/parse"
self.create_master()
d = self.master.loadConfig(config_1)
# that's it, just make sure this config file is loaded successfully
return d

def testRun_Fail(self):
# add an extra status target to make pay attention to which builds
# start and which don't.
self.logger = Logger(self.master)

# kick off upstream1, which has a failing Builder and thus will not
# trigger downstream3
s = self.findScheduler("upstream1")
# this is an internal function of the Scheduler class
s.fireTimer() # fires a build
# t=0: two builders start: 'slowpass' and 'fastfail'
# t=1: builder 'fastfail' finishes
# t=2: builder 'slowpass' finishes
d = defer.Deferred()
self.basedir = "dependencies/Dependencies/run_fail"
self.create_master()
d = self.master.loadConfig(config_1)
d.addCallback(lambda ign:
self.connectSlave(["slowpass", "fastfail", "fastpass",
"b3", "b4", "b5"]))
def _then(ign):
# add an extra status target to make pay attention to which builds
# start and which don't.
self.logger = Logger(self.master)

# kick off upstream1, which has a failing Builder and thus will
# not trigger downstream3. We hit upstream1 (and not upstream3)
# by using a Change with some files.
c = Change("warner", ["foo.py"], "comments")
self.master.change_svc.addChange(c)
# t=0: two builders start: 'slowpass' and 'fastfail'
# t=1: builder 'fastfail' finishes
# t=2: builder 'slowpass' finishes
d.addCallback(_then)
d.addCallback(self.stall, 5.0)
d.addCallback(self._testRun_Fail_1)
reactor.callLater(5, d.callback, None)
return d

def _testRun_Fail_1(self, res):
Expand All @@ -122,23 +125,29 @@ def _testRun_Fail_1(self, res):
self.failIf("b5" in self.logger.builds)

def testRun_Pass(self):
# kick off upstream3, which will fire downstream4 and then
# downstream5
s = self.findScheduler("upstream3")
# this is an internal function of the Scheduler class
s.fireTimer() # fires a build
# t=0: slowpass and fastpass start
# t=1: builder 'fastpass' finishes
# t=2: builder 'slowpass' finishes
# scheduler 'downstream4' fires
# builds b3 and b4 are started
# t=3: builds b3 and b4 finish
# scheduler 'downstream5' fires
# build b5 is started
# t=4: build b5 is finished
d = defer.Deferred()
self.basedir = "dependencies/Dependencies/run_pass"
self.create_master()
d = self.master.loadConfig(config_1)
d.addCallback(lambda ign:
self.connectSlave(["slowpass", "fastfail", "fastpass",
"b3", "b4", "b5"]))
def _then(ign):
# kick off upstream3, which will fire downstream4 and then
# downstream5
c = Change("warner", [], "comments")
self.master.change_svc.addChange(c)
# t=0: slowpass and fastpass start
# t=1: builder 'fastpass' finishes
# t=2: builder 'slowpass' finishes
# scheduler 'downstream4' fires
# builds b3 and b4 are started
# t=3: builds b3 and b4 finish
# scheduler 'downstream5' fires
# build b5 is started
# t=4: build b5 is finished
d.addCallback(_then)
d.addCallback(self.stall, 5.0)
d.addCallback(self._testRun_Pass_1)
reactor.callLater(5, d.callback, None)
return d

def _testRun_Pass_1(self, res):
Expand All @@ -164,5 +173,3 @@ def _testRun_Pass_1(self, res):
b = self.status.getBuilder('b4').getLastFinishedBuild()
self.failUnless(b)
self.failUnlessEqual(b.getNumber(), 0)


44 changes: 18 additions & 26 deletions buildbot/test/runs/test_ec2buildslave.py
Expand Up @@ -5,12 +5,9 @@
import textwrap

from twisted.trial import unittest
from twisted.internet import defer, reactor

from buildbot.process.base import BuildRequest
from buildbot.sourcestamp import SourceStamp
from buildbot.status.builder import SUCCESS
from buildbot.test.runutils import RunMixin
from buildbot.test.runutils import RunMixin, StallMixin


PENDING = 'pending'
Expand Down Expand Up @@ -216,13 +213,10 @@ def connect_ec2(self, identifier, secret_identifier):
exception = Stub(EC2ResponseError=EC2ResponseError)


class Mixin(RunMixin):
class Mixin(RunMixin, StallMixin):

def doBuild(self):
br = BuildRequest("forced", SourceStamp(), 'test_builder')
d = br.waitUntilFinished()
self.control.getBuilder('b1').requestBuild(br)
return d
return self.requestBuild("b1")

def setUp(self):
self.boto_setUp1()
Expand All @@ -249,7 +243,6 @@ def boto_setUp2(self):
del sys.modules['boto.exception']

def boto_setUp3(self):
self.master.startService()
self.boto.slave = self.bot1 = self.master.botmaster.slaves['bot1']
self.bot1._poll_resolution = 0.1
self.b1 = self.master.botmaster.builders['b1']
Expand Down Expand Up @@ -306,9 +299,15 @@ def testSequence(self):
# let's start a build...
self.build_deferred = self.doBuild()
# ...and wait for the ec2 slave to show up
d = self.bot1.substantiation_deferred
d = self.stall(None, 1.0)
d.addCallback(lambda ign: self.bot1.substantiation_deferred)
d.addCallback(self._testSequence_1)
d.addCallback(lambda ign: self.build_deferred)
d.addCallback(self._testSequence_2)
d.addCallback(self.stall, 0.5)
d.addCallback(self._testSequence_3)
return d

def _testSequence_1(self, res):
# bot 1 is substantiated.
self.assertNotIdentical(self.bot1.slave, None)
Expand All @@ -321,11 +320,8 @@ def _testSequence_1(self, res):
['latent_buildbot_slave'])
self.assertEqual(self.bot1.instance.instance_type, 'm1.large')
self.assertEqual(self.bot1.output.output, 'example_output')
# now we'll wait for the build to complete
d = self.build_deferred
del self.build_deferred
d.addCallback(self._testSequence_2)
return d
# now we'll wait for the build to complete, if it hasn't already

def _testSequence_2(self, res):
# build was a success!
self.failUnlessEqual(res.getResults(), SUCCESS)
Expand All @@ -336,10 +332,7 @@ def _testSequence_2(self, res):
# we'll stash the instance around to look at it
self.instance = self.bot1.instance
# now we wait.
d = defer.Deferred()
reactor.callLater(0.5, d.callback, None)
d.addCallback(self._testSequence_3)
return d

def _testSequence_3(self, res):
# slave is insubstantiated
self.assertIdentical(self.bot1.slave, None)
Expand Down Expand Up @@ -380,6 +373,8 @@ def testSequence(self):
# let's start a build...
d = self.doBuild()
d.addCallback(self._testSequence_1)
d.addCallback(self.stall, 0.5)
d.addCallback(self._testSequence_2)
return d
def _testSequence_1(self, res):
# build was a success!
Expand All @@ -391,10 +386,7 @@ def _testSequence_1(self, res):
# Let's let it shut down. We'll set the build_wait_timer to fire
# sooner, and wait for it to fire.
self.bot1.build_wait_timer.reset(0)
d = defer.Deferred()
reactor.callLater(0.5, d.callback, None)
d.addCallback(self._testSequence_2)
return d

def _testSequence_2(self, res):
# slave is insubstantiated
self.assertIdentical(self.bot1.slave, None)
Expand Down Expand Up @@ -422,7 +414,7 @@ def testDefaultSeparateFile(self):
else:
home = None

fake_home = os.path.join(os.getcwd(), 'basedir') # see RunMixin.setUp
fake_home = self.basedir
os.environ['HOME'] = fake_home
dir = os.path.join(fake_home, '.ec2')
os.mkdir(dir)
Expand All @@ -445,7 +437,7 @@ def testDefaultSeparateFile(self):

def testCustomSeparateFile(self):
# set up .ec2/aws_id
file_path = os.path.join(os.getcwd(), 'basedir', 'custom_aws_id')
file_path = os.path.join(self.basedir, 'custom_aws_id')
f = open(file_path, 'w')
f.write('publickey\nprivatekey')
f.close()
Expand Down

0 comments on commit ff021f6

Please sign in to comment.