From 1474639516038656d96462dc0d7ead0d7ddb76d6 Mon Sep 17 00:00:00 2001 From: Thomas Desveaux Date: Fri, 7 Jun 2024 02:49:41 +0200 Subject: [PATCH] add_test --- .../integration/test_process_botmaster.py | 140 ++++++++++++++++++ 1 file changed, 140 insertions(+) diff --git a/master/buildbot/test/integration/test_process_botmaster.py b/master/buildbot/test/integration/test_process_botmaster.py index 4e450fab434f..63b589e248f0 100644 --- a/master/buildbot/test/integration/test_process_botmaster.py +++ b/master/buildbot/test/integration/test_process_botmaster.py @@ -17,8 +17,13 @@ from twisted.internet import defer from buildbot.config import BuilderConfig +from buildbot.data import resultspec from buildbot.process.factory import BuildFactory +from buildbot.process.results import SKIPPED +from buildbot.process.results import SUCCESS from buildbot.process.workerforbuilder import PingException +from buildbot.schedulers import triggerable +from buildbot.steps import trigger from buildbot.test.fake.worker import WorkerController from buildbot.test.util.integration import RunFakeMasterTestCase @@ -57,3 +62,138 @@ def test_terminates_ping_on_shutdown_quick_mode(self): def test_terminates_ping_on_shutdown_slow_mode(self): return self.do_terminates_ping_on_shutdown(quick_mode=False) + + async def test_shutdown_busy_with_child(self): + """ + Test that clean shutdown complete correctly + even when a running Build trigger another + and wait for it's completion + """ + + parent_controller = WorkerController(self, 'parent_worker') + child_controller = WorkerController(self, 'child_worker') + + config_dict = { + 'builders': [ + BuilderConfig( + name="parent", + workernames=[parent_controller.worker.name], + factory=BuildFactory([ + trigger.Trigger(schedulerNames=['triggerable'], waitForFinish=True) + ]), + ), + BuilderConfig( + name="child", workernames=[child_controller.worker.name], factory=BuildFactory() + ), + ], + 'workers': [parent_controller.worker, child_controller.worker], + 'schedulers': [triggerable.Triggerable(name='triggerable', builderNames=['child'])], + 'protocols': {'null': {}}, + 'multiMaster': True, + 'collapseRequests': False, + } + await self.setup_master(config_dict) + + parent_builder_id = await self.master.data.updates.findBuilderId('parent') + child_builder_id = await self.master.data.updates.findBuilderId('child') + + await parent_controller.connect_worker() + # Pause worker of Child builder so we know the build won't start before we start shutdown + await child_controller.disconnect_worker() + + # Create a Child build without Parent so we can later make sure it was not executed + _, first_child_brids = await self.create_build_request([child_builder_id]) + self.assertEqual(len(first_child_brids), 1) + + _, _parent_brids = await self.create_build_request([parent_builder_id]) + self.assertEqual(len(_parent_brids), 1) + parent_brid = _parent_brids[parent_builder_id] + + WAIT_TIMEOUT_SECONDS = 5 + WAIT_STEP_SECONDS = 0.1 + + def _wait_step(): + for _ in range(0, int(WAIT_TIMEOUT_SECONDS * 1000), int(WAIT_STEP_SECONDS * 1000)): + self.reactor.advance(WAIT_STEP_SECONDS) + yield + + # wait until Parent trigger it's Child build + parent_buildid = None + for _ in _wait_step(): + builds = await self.master.data.get( + ("builds",), + filters=[resultspec.Filter('buildrequestid', 'eq', [parent_brid])], + ) + if builds: + self.assertEqual(len(builds), 1) + parent_buildid = builds[0]['buildid'] + break + self.assertIsNotNone(parent_buildid) + + # now get the child_buildset + child_buildsetid = None + for _ in _wait_step(): + buildsets = await self.master.data.get( + ("buildsets",), + filters=[resultspec.Filter('parent_buildid', 'eq', [parent_buildid])], + ) + if buildsets: + self.assertEqual(len(buildsets), 1) + child_buildsetid = buildsets[0]['bsid'] + break + self.assertIsNotNone(child_buildsetid) + + # and finally, the child BuildReques + child_buildrequest = None + for _ in _wait_step(): + buildrequests = await self.master.data.get( + ("buildrequests",), + filters=[resultspec.Filter('buildsetid', 'eq', [child_buildsetid])], + ) + if buildrequests: + self.assertEqual(len(buildrequests), 1) + child_buildrequest = buildrequests[0] + break + self.assertIsNotNone(child_buildrequest) + + # create a second Child without Parent for good mesure + _, second_child_brids = await self.create_build_request([child_builder_id]) + self.assertEqual(len(second_child_brids), 1) + + # Now start the clean shutdown + shutdown_deferred: defer.Deferred[None] = self.master.botmaster.cleanShutdown( + quickMode=False, + stopReactor=False, + ) + + # Connect back Child worker so the build can happen + await child_controller.connect_worker() + + # wait for the child request to be claimed, and completed + for _ in _wait_step(): + if child_buildrequest['claimed'] and child_buildrequest['complete']: + break + child_buildrequest = await self.master.data.get( + ("buildrequests", child_buildrequest['buildrequestid']), + ) + self.assertIsNotNone(child_buildrequest) + self.assertEqual(child_buildrequest['results'], SUCCESS) + + # make sure parent-less BuildRequest weren't built + first_child_request = await self.master.data.get( + ("buildrequests", first_child_brids[child_builder_id]), + ) + self.assertIsNotNone(first_child_request) + self.assertFalse(first_child_request['claimed']) + self.assertFalse(first_child_request['complete']) + + second_child_request = await self.master.data.get( + ("buildrequests", second_child_brids[child_builder_id]), + ) + self.assertIsNotNone(second_child_request) + self.assertFalse(second_child_request['claimed']) + self.assertFalse(second_child_request['complete']) + + # confirm Master shutdown + await shutdown_deferred + self.assertTrue(shutdown_deferred.called)