예제 #1
0
파일: test_db.py 프로젝트: frelo/buildbot
    def test_stabletimer(self):
        m, cm, sm = self.build_harness("db/scheduler/stabletimer")
        s = Scheduler("one", branch=None, treeStableTimer=30,
                      builderNames=["builder-one"])
        d = sm.updateSchedulers([s])

        # there are no changes in the database, so the scheduler should want
        # to keep sleeping.
        d.addCallback(lambda ign: s.run())
        def _sleep_forever_not_build(res):
            self.failUnlessEqual(res, None)
            pending = self.get_buildrequests(m, cm)
            self.failIf(pending)
        d.addCallback(_sleep_forever_not_build)

        # now add one change. The scheduler should want to wait for the
        # tree-stable timer to fire.
        c1 = Change(who="brian", files=["foo.c"],
                    comments="first change",
                    revision="1234")
        d.addCallback(lambda ign: cm.addChange(c1))
        d.addCallback(lambda ign: s.run())
        def _sleep_not_build(res):
            # the scheduler should tell us that they want to be woken up in
            # 30 seconds. This test would be much too fragile if we actually
            # asserted anything about its value, though.
            self.failUnlessEqual(type(res), float)
            pending = self.get_buildrequests(m, cm)
            self.failIf(pending)
        d.addCallback(_sleep_not_build)

        # running it again should tell us roughly the same thing
        d.addCallback(lambda ign: s.run())
        d.addCallback(_sleep_not_build)
        def _reduce_timer(res):
            # artificially lower the tree-stable-timer value
            s.treeStableTimer = 0
        d.addCallback(_reduce_timer)
        d.addCallback(lambda ign: s.run())
        def _build_not_sleep(res):
            # a BuildRequest should be pushed, and the Scheduler should go
            # back to sleep
            self.failUnlessEqual(res, None)
            pending = self.get_buildrequests(m, cm)
            self.failUnlessEqual(len(pending), 1)
            changes = pending[0][1].changes
            self.failUnlessEqual(len(changes), 1)
            self.failUnlessEqual(changes[0].revision, "1234")
            self.failUnlessEqual(changes[0].files, ["foo.c"])
        d.addCallback(_build_not_sleep)

        return d
예제 #2
0
파일: test_db.py 프로젝트: frelo/buildbot
    def test_many_changes(self):
        m, cm, sm = self.build_harness("db/scheduler/many_changes")
        s = Scheduler("one", branch=None, treeStableTimer=0.01,
                      builderNames=["builder-one"])
        d = sm.updateSchedulers([s])

        # add ten changes, then process them all at once. Sometimes the
        # database connector has problems with lots of queries happening
        # simultaneously.
        for i in range(10):
            c = Change(who="brian", files=["foo%d.txt" % i],
                       comments="change %d" % i,
                       revision="%d" % (i+10))
            d.addCallback(lambda ign, c=c: cm.addChange(c))
        d.addCallback(self.stall, 1.0)
        d.addCallback(lambda ign: s.run())
        def _build_not_sleep(res):
            # a BuildRequest should be pushed, and the Scheduler should go
            # back to sleep
            self.failUnlessEqual(res, None)
            pending = self.get_buildrequests(m, cm)
            self.failUnlessEqual(len(pending), 1)
            changes = pending[0][1].changes
            self.failUnlessEqual(len(changes), 10)
        d.addCallback(_build_not_sleep)

        return d
예제 #3
0
class TestTestOrder(unittest.TestCase):
    """Tests that tests are run according to their corresponding build's start
    time"""

    basedir = "test_test_order"

    def setUp(self):
        if os.path.exists(self.basedir):
            shutil.rmtree(self.basedir)
        os.makedirs(self.basedir)
        spec = dbspec.DBSpec.from_url("sqlite:///state.sqlite", self.basedir)
        manager = DBSchemaManager(spec, self.basedir)
        manager.upgrade()

        self.dbc = connector.DBConnector(spec)
        self.dbc.start()

        parent = mock.Mock()
        parent.db = self.dbc

        change_manager = ChangeManager()
        change_manager.parent = parent

        parent.change_svc = change_manager

        self.s = Scheduler(name="s", builderNames=["b1"])
        self.s.parent = parent


        return self.dbc.addSchedulers([self.s])

    def makeBuilder(self, klass):
        dummy_factory = klass([Dummy()])
        builder = {
                'name': 'b1',
                'slavenames': ['s1'],
                'builddir': 'b1',
                'slavebuilddir': 'b1',
                'factory': dummy_factory,
                }
        builder_status = mock.Mock()

        builder = Builder(builder, builder_status)
        builder.running = True
        builder.db = self.dbc
        builder.master_name = 'test_master'
        builder.master_incarnation = '12345'
        builder.botmaster = mock.Mock()
        builder.botmaster.shouldMergeRequests.return_value = True
        return builder

    def tearDown(self):
        self.dbc.stop()
        shutil.rmtree(self.basedir)

    def test_singleRequest(self):
        # Easy peasy
        c1 = Change(who='me!', branch='b1', revision='1', files=['http://path/to/build'],
                comments='really important', properties={'buildid': '20110214000001'})
        self.dbc.addChangeToDatabase(c1)

        builder = self.makeBuilder(BuildFactory)

        # Run the scheduler
        d = self.s.run()

        # Check that we have a build request
        def checkRequests(ign):
            req = self.dbc.runQueryNow("select count(*) from buildrequests")
            self.assertEquals(req[0][0], 1)
        d.addCallback(checkRequests)

        # Claim the request
        def claimRequest(ign):
            d = self.dbc.runInteraction(builder._claim_buildreqs, ["slave!"])
            return d
        d.addCallback(claimRequest)

        # Start the build!
        def startBuild(assignments):
            self.assertEquals(len(assignments.keys()), 1)
            requests = assignments.values()[0]
            self.assertEquals(len(requests), 1)

            build = builder.buildFactory.newBuild(requests)
            self.assertEquals(build.source.revision, '1')

            changes = build.source.changes
            self.assertEquals(changes[0].revision, '1')
            return
        d.addCallback(startBuild)

        return d

    def test_threeRequests(self):
        # Three changes, r1, r2, r3
        # They start in order, and finish in reverse order
        # We want the to be testing r3 in the end
        c1 = Change(who='me!', branch='b1', revision='r3', files=['http://path/to/build'],
                comments='really important', properties={'buildid': '20110214000003'}, when=4)
        c2 = Change(who='me!', branch='b1', revision='r2', files=['http://path/to/build'],
                comments='really important', properties={'buildid': '20110214000002'}, when=5)
        c3 = Change(who='me!', branch='b1', revision='r1', files=['http://path/to/build'],
                comments='really important', properties={'buildid': '20110214000001'}, when=6)
        self.dbc.addChangeToDatabase(c1)
        self.dbc.addChangeToDatabase(c2)
        self.dbc.addChangeToDatabase(c3)

        # Run the scheduler
        d = self.s.run()

        builder = self.makeBuilder(RequestSortingBuildFactory)

        # Check that we have three build requests
        def checkRequests(ign):
            req = self.dbc.runQueryNow("select count(*) from buildrequests")
            self.assertEquals(req[0][0], 3)
        d.addCallback(checkRequests)

        # Claim the request
        def claimRequest(ign):
            d = self.dbc.runInteraction(builder._claim_buildreqs, ["slave!"])
            return d
        d.addCallback(claimRequest)

        # Start the build!
        def startBuild(assignments):
            self.assertEquals(len(assignments.keys()), 1)
            requests = assignments.values()[0]
            self.assertEquals(len(requests), 3)

            build = builder.buildFactory.newBuild(requests)
            self.assertEquals(build.source.revision, 'r3')

            changes = build.source.changes
            # The last revision should be r3, since it started latest
            self.assertEquals(changes[0].revision, 'r1')
            self.assertEquals(changes[1].revision, 'r2')
            self.assertEquals(changes[2].revision, 'r3')
            return
        d.addCallback(startBuild)
        return d

    def test_threeRequestsUnsorted(self):
        # Three changes, r1, r2, r3
        # They start in order, and finish in reverse order
        # We want the to be testing r3 in the end
        c1 = Change(who='me!', branch='b1', revision='r3', files=['http://path/to/build'],
                comments='really important', properties={'buildid': '20110214000003'}, when=4)
        c2 = Change(who='me!', branch='b1', revision='r2', files=['http://path/to/build'],
                comments='really important', properties={'buildid': '20110214000002'}, when=5)
        c3 = Change(who='me!', branch='b1', revision='r1', files=['http://path/to/build'],
                comments='really important', properties={'buildid': '20110214000001'}, when=6)
        self.dbc.addChangeToDatabase(c1)
        self.dbc.addChangeToDatabase(c2)
        self.dbc.addChangeToDatabase(c3)

        # Run the scheduler
        d = self.s.run()

        builder = self.makeBuilder(BuildFactory)

        # Check that we have three build requests
        def checkRequests(ign):
            req = self.dbc.runQueryNow("select count(*) from buildrequests")
            self.assertEquals(req[0][0], 3)
        d.addCallback(checkRequests)

        # Claim the request
        def claimRequest(ign):
            d = self.dbc.runInteraction(builder._claim_buildreqs, ["slave!"])
            return d
        d.addCallback(claimRequest)

        # Start the build!
        def startBuild(assignments):
            self.assertEquals(len(assignments.keys()), 1)
            requests = assignments.values()[0]
            self.assertEquals(len(requests), 3)

            build = builder.buildFactory.newBuild(requests)
            self.assertEquals(build.source.revision, 'r1')

            changes = build.source.changes
            # The last revision should be r1, since it was submitted latest
            self.assertEquals(changes[0].revision, 'r3')
            self.assertEquals(changes[1].revision, 'r2')
            self.assertEquals(changes[2].revision, 'r1')
            return
        d.addCallback(startBuild)
        return d

    def test_threeRequestsManual(self):
        # Three changes, r1, r2, r3
        # r2, and r3 have buildids
        # r1 is manually triggered, and is submitted later
        # we want to be testing r1 in the end
        c1 = Change(who='me!', branch='b1', revision='r3', files=['http://path/to/build'],
                comments='really important', properties={'buildid': '20110214000003'}, when=4)
        c2 = Change(who='me!', branch='b1', revision='r2', files=['http://path/to/build'],
                comments='really important', properties={'buildid': '20110214000002'}, when=5)
        c3 = Change(who='me!', branch='b1', revision='r1', files=['http://path/to/build'],
                comments='really important', when=6)
        self.dbc.addChangeToDatabase(c1)
        self.dbc.addChangeToDatabase(c2)
        self.dbc.addChangeToDatabase(c3)

        # Run the scheduler
        d = self.s.run()

        builder = self.makeBuilder(RequestSortingBuildFactory)

        # Check that we have three build requests
        def checkRequests(ign):
            req = self.dbc.runQueryNow("select count(*) from buildrequests")
            self.assertEquals(req[0][0], 3)
        d.addCallback(checkRequests)

        # Claim the request
        def claimRequest(ign):
            d = self.dbc.runInteraction(builder._claim_buildreqs, ["slave!"])
            return d
        d.addCallback(claimRequest)

        # Start the build!
        def startBuild(assignments):
            self.assertEquals(len(assignments.keys()), 1)
            requests = assignments.values()[0]
            self.assertEquals(len(requests), 3)

            build = builder.buildFactory.newBuild(requests)
            self.assertEquals(build.source.revision, 'r1')

            changes = build.source.changes
            # The last revision should be r1, since it was manually triggered later
            self.assertEquals(changes[0].revision, 'r2')
            self.assertEquals(changes[1].revision, 'r3')
            self.assertEquals(changes[2].revision, 'r1')
            return
        d.addCallback(startBuild)
        return d

    def test_Rebuilds(self):
        # Two changes, r2, r3
        # They start in order, and finish in reverse order
        # We also have a pending request to re-build r1 that happens after r2, r3 have started
        # We want to end up re-building r1
        c1 = Change(who='me!', branch='b1', revision='r3', files=['http://path/to/build'],
                comments='really important', properties={'buildid': '20110214000003'}, when=4)
        c2 = Change(who='me!', branch='b1', revision='r2', files=['http://path/to/build'],
                comments='really important', properties={'buildid': '20110214000002'}, when=5)
        self.dbc.addChangeToDatabase(c1)
        self.dbc.addChangeToDatabase(c2)

        # Run the scheduler
        d = self.s.run()

        # Rebuild r1
        def addRebuild(t):
            c3 = Change(who='me!', branch='b1', revision='r1', files=['http://path/to/build'],
                    comments='really important', properties={'buildid': '20110214000001'}, when=6)
            self.dbc.addChangeToDatabase(c3)
            ss = SourceStamp(branch='b1', changes=[c3], revision='r1')
            ss1 = ss.getAbsoluteSourceStamp('r1')
            ssid = self.dbc.get_sourcestampid(ss, t)
            bsid = self.dbc.create_buildset(ssid, "rebuild", Properties(), ["b1"], t)

        d.addCallback(lambda ign: self.dbc.runInteractionNow(addRebuild))

        builder = self.makeBuilder(RequestSortingBuildFactory)

        # Check that we have three build requests
        def checkRequests(ign):
            req = self.dbc.runQueryNow("select count(*) from buildrequests")
            self.assertEquals(req[0][0], 3)
        d.addCallback(checkRequests)

        # Claim the request
        def claimRequest(ign):
            d = self.dbc.runInteraction(builder._claim_buildreqs, ["slave!"])
            return d
        d.addCallback(claimRequest)

        # Start the build!
        def startBuild(assignments):
            self.assertEquals(len(assignments.keys()), 1)
            requests = assignments.values()[0]
            self.assertEquals(len(requests), 3)

            build = builder.buildFactory.newBuild(requests)
            self.assertEquals(build.source.revision, 'r1')

            changes = build.source.changes
            # The last revision should be r1, since it was manually triggered later
            self.assertEquals(changes[0].revision, 'r2')
            self.assertEquals(changes[1].revision, 'r3')
            self.assertEquals(changes[2].revision, 'r1')
            return
        d.addCallback(startBuild)
        return d

    def test_brokenRequest(self):
        c1 = Change(who='me!', branch='b1', revision='1', files=['http://path/to/build'],
                comments='really important', properties={'buildid': '20110214000001'})
        self.dbc.addChangeToDatabase(c1)

        builder = self.makeBuilder(RequestSortingBuildFactory)

        # Run the scheduler
        d = self.s.run()

        # Check that we have a build request
        def checkRequests(ign):
            req = self.dbc.runQueryNow("select count(*) from buildrequests")
            self.assertEquals(req[0][0], 1)
        d.addCallback(checkRequests)

        # Claim the request
        def claimRequest(ign):
            d = self.dbc.runInteraction(builder._claim_buildreqs, ["slave!"])
            return d
        d.addCallback(claimRequest)

        # Start the build!
        def startBuild(assignments):
            self.assertEquals(len(assignments.keys()), 1)
            requests = assignments.values()[0]
            # Hack the request to break the sorting
            requests[0].submittedAt = "asdf"
            requests[0].reason = "rebuild"

            self.assertEquals(len(requests), 1)

            build = builder.buildFactory.newBuild(requests)
            # This should have generated an error
            self.failUnless(len(self.flushLoggedErrors()) == 1)
            self.assertEquals(build.source.revision, '1')

            changes = build.source.changes
            self.assertEquals(changes[0].revision, '1')
            return
        d.addCallback(startBuild)

        return d
예제 #4
0
파일: test_db.py 프로젝트: frelo/buildbot
    def test_immediate(self):
        m, cm, sm = self.build_harness("db/scheduler/immediate")
        def fileIsImportant(c):
            for fn in c.files:
                if not fn.endswith(".txt"):
                    return True
            return False
        # we set the treeStableTimer to something tiny, since "None" has a
        # special meaning ("do not merge Changes")
        s = Scheduler("one", branch=None, treeStableTimer=0.01,
                      builderNames=["builder-one"],
                      fileIsImportant=fileIsImportant)
        d = sm.updateSchedulers([s])

        # there are no changes in the database, so the scheduler should want
        # to keep sleeping.
        d.addCallback(lambda ign: s.run())
        def _sleep_forever_not_build(res):
            self.failUnlessEqual(res, None)
            pending = self.get_buildrequests(m, cm)
            self.failIf(pending)
        d.addCallback(_sleep_forever_not_build)

        # now add one change. The change is "unimportant", so no build will
        # be run.
        c1 = Change(who="brian", files=["docs.txt"],
                    comments="doc change",
                    revision="1234")
        d.addCallback(lambda ign: cm.addChange(c1))
        d.addCallback(lambda ign: s.run())
        d.addCallback(_sleep_forever_not_build)
        # running it again should tell us the same thing
        d.addCallback(lambda ign: s.run())
        d.addCallback(_sleep_forever_not_build)

        # now add a second change which evaluates as "important", which
        # should trigger a build with both changes after the treeStableTimer
        # has passed, which should be quickly
        c2 = Change(who="brian", files=["foo.c", "subdir/bar.c"],
                    comments="second change",
                    revision="1235")
        d.addCallback(lambda ign: cm.addChange(c2))
        # stall here to let the treeStableTimer expire
        d.addCallback(self.stall, 1.0)
        d.addCallback(lambda ign: s.run())
        def _build_not_sleep(res):
            # a BuildRequest should be pushed, and the Scheduler should go
            # back to sleep
            self.failUnlessEqual(res, None)
            pending = self.get_buildrequests(m, cm)
            self.failUnlessEqual(len(pending), 1)
            changes = pending[0][1].changes
            self.failUnlessEqual(len(changes), 2)
            self.failUnlessEqual(changes[0].revision, "1234")
            self.failUnlessEqual(changes[0].files, ["docs.txt"])
            self.failUnlessEqual(changes[1].revision, "1235")
            self.failUnlessIn("foo.c", changes[1].files)
            self.failUnlessIn("subdir/bar.c", changes[1].files)
        d.addCallback(_build_not_sleep)
        # running it again should not do anything
        d.addCallback(lambda ign: s.run())
        def _build_and_sleep(res):
            self.failUnlessEqual(res, None)
            pending = self.get_buildrequests(m, cm)
            self.failUnlessEqual(len(pending), 1)
        d.addCallback(_build_and_sleep)

        return d
예제 #5
0
class TestTestOrder(unittest.TestCase):
    """Tests that tests are run according to their corresponding build's start
    time"""

    basedir = "test_test_order"

    def setUp(self):
        if os.path.exists(self.basedir):
            shutil.rmtree(self.basedir)
        os.makedirs(self.basedir)
        spec = dbspec.DBSpec.from_url("sqlite:///state.sqlite", self.basedir)
        manager = DBSchemaManager(spec, self.basedir)
        manager.upgrade()

        self.dbc = connector.DBConnector(spec)
        self.dbc.start()

        parent = mock.Mock()
        parent.db = self.dbc

        change_manager = ChangeManager()
        change_manager.parent = parent

        parent.change_svc = change_manager

        self.s = Scheduler(name="s", builderNames=["b1"])
        self.s.parent = parent

        return self.dbc.addSchedulers([self.s])

    def makeBuilder(self, klass):
        dummy_factory = klass([Dummy()])
        builder = {
            'name': 'b1',
            'slavenames': ['s1'],
            'builddir': 'b1',
            'slavebuilddir': 'b1',
            'factory': dummy_factory,
        }
        builder_status = mock.Mock()

        builder = Builder(builder, builder_status)
        builder.running = True
        builder.db = self.dbc
        builder.master_name = 'test_master'
        builder.master_incarnation = '12345'
        builder.botmaster = mock.Mock()
        builder.botmaster.shouldMergeRequests.return_value = True
        return builder

    def tearDown(self):
        self.dbc.stop()
        shutil.rmtree(self.basedir)

    def test_singleRequest(self):
        # Easy peasy
        c1 = Change(who='me!',
                    branch='b1',
                    revision='1',
                    files=['http://path/to/build'],
                    comments='really important',
                    properties={'buildid': '20110214000001'})
        self.dbc.addChangeToDatabase(c1)

        builder = self.makeBuilder(BuildFactory)

        # Run the scheduler
        d = self.s.run()

        # Check that we have a build request
        def checkRequests(ign):
            req = self.dbc.runQueryNow("select count(*) from buildrequests")
            self.assertEquals(req[0][0], 1)

        d.addCallback(checkRequests)

        # Claim the request
        def claimRequest(ign):
            d = self.dbc.runInteraction(builder._claim_buildreqs, ["slave!"])
            return d

        d.addCallback(claimRequest)

        # Start the build!
        def startBuild(assignments):
            self.assertEquals(len(assignments.keys()), 1)
            requests = assignments.values()[0]
            self.assertEquals(len(requests), 1)

            build = builder.buildFactory.newBuild(requests)
            self.assertEquals(build.source.revision, '1')

            changes = build.source.changes
            self.assertEquals(changes[0].revision, '1')
            return

        d.addCallback(startBuild)

        return d

    def test_threeRequests(self):
        # Three changes, r1, r2, r3
        # They start in order, and finish in reverse order
        # We want the to be testing r3 in the end
        c1 = Change(who='me!',
                    branch='b1',
                    revision='r3',
                    files=['http://path/to/build'],
                    comments='really important',
                    properties={'buildid': '20110214000003'},
                    when=4)
        c2 = Change(who='me!',
                    branch='b1',
                    revision='r2',
                    files=['http://path/to/build'],
                    comments='really important',
                    properties={'buildid': '20110214000002'},
                    when=5)
        c3 = Change(who='me!',
                    branch='b1',
                    revision='r1',
                    files=['http://path/to/build'],
                    comments='really important',
                    properties={'buildid': '20110214000001'},
                    when=6)
        self.dbc.addChangeToDatabase(c1)
        self.dbc.addChangeToDatabase(c2)
        self.dbc.addChangeToDatabase(c3)

        # Run the scheduler
        d = self.s.run()

        builder = self.makeBuilder(RequestSortingBuildFactory)

        # Check that we have three build requests
        def checkRequests(ign):
            req = self.dbc.runQueryNow("select count(*) from buildrequests")
            self.assertEquals(req[0][0], 3)

        d.addCallback(checkRequests)

        # Claim the request
        def claimRequest(ign):
            d = self.dbc.runInteraction(builder._claim_buildreqs, ["slave!"])
            return d

        d.addCallback(claimRequest)

        # Start the build!
        def startBuild(assignments):
            self.assertEquals(len(assignments.keys()), 1)
            requests = assignments.values()[0]
            self.assertEquals(len(requests), 3)

            build = builder.buildFactory.newBuild(requests)
            self.assertEquals(build.source.revision, 'r3')

            changes = build.source.changes
            # The last revision should be r3, since it started latest
            self.assertEquals(changes[0].revision, 'r1')
            self.assertEquals(changes[1].revision, 'r2')
            self.assertEquals(changes[2].revision, 'r3')
            return

        d.addCallback(startBuild)
        return d

    def test_threeRequestsUnsorted(self):
        # Three changes, r1, r2, r3
        # They start in order, and finish in reverse order
        # We want the to be testing r3 in the end
        c1 = Change(who='me!',
                    branch='b1',
                    revision='r3',
                    files=['http://path/to/build'],
                    comments='really important',
                    properties={'buildid': '20110214000003'},
                    when=4)
        c2 = Change(who='me!',
                    branch='b1',
                    revision='r2',
                    files=['http://path/to/build'],
                    comments='really important',
                    properties={'buildid': '20110214000002'},
                    when=5)
        c3 = Change(who='me!',
                    branch='b1',
                    revision='r1',
                    files=['http://path/to/build'],
                    comments='really important',
                    properties={'buildid': '20110214000001'},
                    when=6)
        self.dbc.addChangeToDatabase(c1)
        self.dbc.addChangeToDatabase(c2)
        self.dbc.addChangeToDatabase(c3)

        # Run the scheduler
        d = self.s.run()

        builder = self.makeBuilder(BuildFactory)

        # Check that we have three build requests
        def checkRequests(ign):
            req = self.dbc.runQueryNow("select count(*) from buildrequests")
            self.assertEquals(req[0][0], 3)

        d.addCallback(checkRequests)

        # Claim the request
        def claimRequest(ign):
            d = self.dbc.runInteraction(builder._claim_buildreqs, ["slave!"])
            return d

        d.addCallback(claimRequest)

        # Start the build!
        def startBuild(assignments):
            self.assertEquals(len(assignments.keys()), 1)
            requests = assignments.values()[0]
            self.assertEquals(len(requests), 3)

            build = builder.buildFactory.newBuild(requests)
            self.assertEquals(build.source.revision, 'r1')

            changes = build.source.changes
            # The last revision should be r1, since it was submitted latest
            self.assertEquals(changes[0].revision, 'r3')
            self.assertEquals(changes[1].revision, 'r2')
            self.assertEquals(changes[2].revision, 'r1')
            return

        d.addCallback(startBuild)
        return d

    def test_threeRequestsManual(self):
        # Three changes, r1, r2, r3
        # r2, and r3 have buildids
        # r1 is manually triggered, and is submitted later
        # we want to be testing r1 in the end
        c1 = Change(who='me!',
                    branch='b1',
                    revision='r3',
                    files=['http://path/to/build'],
                    comments='really important',
                    properties={'buildid': '20110214000003'},
                    when=4)
        c2 = Change(who='me!',
                    branch='b1',
                    revision='r2',
                    files=['http://path/to/build'],
                    comments='really important',
                    properties={'buildid': '20110214000002'},
                    when=5)
        c3 = Change(who='me!',
                    branch='b1',
                    revision='r1',
                    files=['http://path/to/build'],
                    comments='really important',
                    when=6)
        self.dbc.addChangeToDatabase(c1)
        self.dbc.addChangeToDatabase(c2)
        self.dbc.addChangeToDatabase(c3)

        # Run the scheduler
        d = self.s.run()

        builder = self.makeBuilder(RequestSortingBuildFactory)

        # Check that we have three build requests
        def checkRequests(ign):
            req = self.dbc.runQueryNow("select count(*) from buildrequests")
            self.assertEquals(req[0][0], 3)

        d.addCallback(checkRequests)

        # Claim the request
        def claimRequest(ign):
            d = self.dbc.runInteraction(builder._claim_buildreqs, ["slave!"])
            return d

        d.addCallback(claimRequest)

        # Start the build!
        def startBuild(assignments):
            self.assertEquals(len(assignments.keys()), 1)
            requests = assignments.values()[0]
            self.assertEquals(len(requests), 3)

            build = builder.buildFactory.newBuild(requests)
            self.assertEquals(build.source.revision, 'r1')

            changes = build.source.changes
            # The last revision should be r1, since it was manually triggered
            # later
            self.assertEquals(changes[0].revision, 'r2')
            self.assertEquals(changes[1].revision, 'r3')
            self.assertEquals(changes[2].revision, 'r1')
            return

        d.addCallback(startBuild)
        return d

    def test_Rebuilds(self):
        # Two changes, r2, r3
        # They start in order, and finish in reverse order
        # We also have a pending request to re-build r1 that happens after r2, r3 have started
        # We want to end up re-building r1
        c1 = Change(who='me!',
                    branch='b1',
                    revision='r3',
                    files=['http://path/to/build'],
                    comments='really important',
                    properties={'buildid': '20110214000003'},
                    when=4)
        c2 = Change(who='me!',
                    branch='b1',
                    revision='r2',
                    files=['http://path/to/build'],
                    comments='really important',
                    properties={'buildid': '20110214000002'},
                    when=5)
        self.dbc.addChangeToDatabase(c1)
        self.dbc.addChangeToDatabase(c2)

        # Run the scheduler
        d = self.s.run()

        # Rebuild r1
        def addRebuild(t):
            c3 = Change(who='me!',
                        branch='b1',
                        revision='r1',
                        files=['http://path/to/build'],
                        comments='really important',
                        properties={'buildid': '20110214000001'},
                        when=6)
            self.dbc.addChangeToDatabase(c3)
            ss = SourceStamp(branch='b1', changes=[c3], revision='r1')
            ssid = self.dbc.get_sourcestampid(ss, t)
            self.dbc.create_buildset(ssid, "rebuild", Properties(), ["b1"], t)

        d.addCallback(lambda ign: self.dbc.runInteractionNow(addRebuild))

        builder = self.makeBuilder(RequestSortingBuildFactory)

        # Check that we have three build requests
        def checkRequests(ign):
            req = self.dbc.runQueryNow("select count(*) from buildrequests")
            self.assertEquals(req[0][0], 3)

        d.addCallback(checkRequests)

        # Claim the request
        def claimRequest(ign):
            d = self.dbc.runInteraction(builder._claim_buildreqs, ["slave!"])
            return d

        d.addCallback(claimRequest)

        # Start the build!
        def startBuild(assignments):
            self.assertEquals(len(assignments.keys()), 1)
            requests = assignments.values()[0]
            self.assertEquals(len(requests), 3)

            build = builder.buildFactory.newBuild(requests)
            self.assertEquals(build.source.revision, 'r1')

            changes = build.source.changes
            # The last revision should be r1, since it was manually triggered
            # later
            self.assertEquals(changes[0].revision, 'r2')
            self.assertEquals(changes[1].revision, 'r3')
            self.assertEquals(changes[2].revision, 'r1')
            return

        d.addCallback(startBuild)
        return d

    def test_brokenRequest(self):
        c1 = Change(who='me!',
                    branch='b1',
                    revision='1',
                    files=['http://path/to/build'],
                    comments='really important',
                    properties={'buildid': '20110214000001'})
        self.dbc.addChangeToDatabase(c1)

        builder = self.makeBuilder(RequestSortingBuildFactory)

        # Run the scheduler
        d = self.s.run()

        # Check that we have a build request
        def checkRequests(ign):
            req = self.dbc.runQueryNow("select count(*) from buildrequests")
            self.assertEquals(req[0][0], 1)

        d.addCallback(checkRequests)

        # Claim the request
        def claimRequest(ign):
            d = self.dbc.runInteraction(builder._claim_buildreqs, ["slave!"])
            return d

        d.addCallback(claimRequest)

        # Start the build!
        def startBuild(assignments):
            self.assertEquals(len(assignments.keys()), 1)
            requests = assignments.values()[0]
            # Hack the request to break the sorting
            requests[0].submittedAt = "asdf"
            requests[0].reason = "rebuild"

            self.assertEquals(len(requests), 1)

            build = builder.buildFactory.newBuild(requests)
            # This should have generated an error
            self.failUnless(len(self.flushLoggedErrors()) == 1)
            self.assertEquals(build.source.revision, '1')

            changes = build.source.changes
            self.assertEquals(changes[0].revision, '1')
            return

        d.addCallback(startBuild)

        return d