async def test_risk_analysis_shouldnt_trigger(PhabricatorMock, mock_taskcluster): bus = MessageBus() build = PhabricatorBuild( MockRequest( diff="125397", repo="PHID-REPO-saax4qdxlbbhahhp2kg5", revision="36474", target="PHID-HMBT-icusvlfibcebizyd33op", )) with PhabricatorMock as phab: client = CodeReview( risk_analysis_reviewers=["ehsan"], url="http://phabricator.test/api/", api_key="fakekey", community_config={ "client_id": "xxx", "access_token": "yyy" }, ) client.register(bus) phab.update_state(build) phab.load_reviewers(build) assert not client.should_run_risk_analysis(build)
def __init__(self): # Create message bus shared amongst process self.bus = MessageBus() # Build code coverage workflow self.workflow = CodeCoverage( taskcluster_config.secrets["hook_id"], taskcluster_config.secrets["hook_group_id"], self.bus, ) # Setup monitoring for newly created tasks self.monitoring = Monitoring( taskcluster_config, QUEUE_MONITORING, taskcluster_config.secrets["admins"], 7 * 3600, ) self.monitoring.register(self.bus) # Create pulse listener for code coverage self.pulse = PulseListener( { QUEUE_PULSE: [ ("exchange/taskcluster-queue/v1/task-group-resolved", ["#"]) ] }, taskcluster_config.secrets["pulse_user"], taskcluster_config.secrets["pulse_password"], ) self.pulse.register(self.bus)
async def test_monitoring_retry_exceptions(QueueMock, NotifyMock, mock_taskcluster): bus = MessageBus() monitoring = Monitoring(mock_taskcluster, "testqueue", ["pinco@pallino"], 1) monitoring.register(bus) await bus.send("testqueue", ("Group1", "Hook1", "Task-exception-retry:2")) await bus.send("testqueue", ("Group1", "Hook2", "Task-exception-retry:0")) assert bus.queues["testqueue"].qsize() == 2 monitoring.queue = QueueMock assert len(monitoring.queue.created_tasks) == 0 monitoring.notify = NotifyMock # Task exception with 2 retries await monitoring.check_task() assert monitoring.stats["Hook1"]["exception"] == ["Task-exception-retry:2"] assert len(monitoring.queue.created_tasks) == 1 assert bus.queues["testqueue"].qsize() == 2 # The retried task should maintain the original taskGroupId old_task = await monitoring.queue.task("Task-exception-retry:2") new_task_id, new_task = monitoring.queue.created_tasks[0] assert new_task_id != "Task-exception-retry:2" assert new_task != old_task assert new_task["taskGroupId"] == old_task["taskGroupId"] assert new_task["payload"] == old_task["payload"] assert new_task["created"] != old_task["created"] # Task exception with 0 retries # No new task should be created await monitoring.check_task() assert monitoring.stats["Hook2"]["exception"] == ["Task-exception-retry:0"] assert len(monitoring.queue.created_tasks) == 1 assert bus.queues["testqueue"].qsize() == 1
async def test_publish_results_success_mode(PhabricatorMock, mock_taskcluster): bus = MessageBus() build = PhabricatorBuild( MockRequest( diff="125397", repo="PHID-REPO-saax4qdxlbbhahhp2kg5", revision="36474", target="PHID-HMBT-icusvlfibcebizyd33op", ) ) with PhabricatorMock as phab: client = CodeReview( publish=True, url="http://phabricator.test/api/", api_key="fakekey" ) client.register(bus) phab.update_state(build) await client.publish_results( ( "success", build, {"treeherder_url": "https://treeherder.org/", "revision": "123"}, ) )
async def test_conversion(): """ Test message conversion between 2 queues """ bus = MessageBus() bus.add_queue("input") bus.add_queue( "output", maxsize=3) # limit size to immediately stop execution for unit test assert isinstance(bus.queues["input"], asyncio.Queue) assert isinstance(bus.queues["output"], asyncio.Queue) assert bus.queues["input"].qsize() == 0 assert bus.queues["output"].qsize() == 0 await bus.send("input", "test x") await bus.send("input", "hello world.") await bus.send("output", "lowercase") # Convert all strings from input in uppercase assert bus.queues["input"].qsize() == 2 task = asyncio.create_task( bus.run(lambda x: x.upper(), "input", ["output"])) await bus.receive("output") == "lowercase" await bus.receive("output") == "TEST X" await bus.receive("output") == "HELLO WORLD." task.cancel() assert bus.queues["input"].qsize() == 0 assert bus.queues["output"].qsize() == 0
def test_wrong_branch(mock_taskcluster): bus = MessageBus() hook = CodeCoverage("services-staging-codecoverage/bot", "project-test", bus) assert run_async_parser(hook, "bNq-VIT-Q12o6nXcaUmYNQ", "gecko-level-1") is None
async def test_risk_analysis_should_trigger(PhabricatorMock, mock_taskcluster): bus = MessageBus() build = PhabricatorBuild( MockRequest( diff="125397", repo="PHID-REPO-saax4qdxlbbhahhp2kg5", revision="36474", target="PHID-HMBT-icusvlfibcebizyd33op", )) with PhabricatorMock as phab: phab.load_patches_stack(build) phab.update_state(build) # Reviewer of the patch is in the list of risk analysis users. taskcluster_config.secrets["risk_analysis_users"] = ["ehsan", "heycam"] bugbug_utils = BugbugUtils(phab.api) bugbug_utils.register(bus) assert bugbug_utils.should_run_risk_analysis(build) # Author of the patch is in the list of risk analysis users. taskcluster_config.secrets["risk_analysis_users"] = [ "ehsan", "tnguyen" ] bugbug_utils = BugbugUtils(phab.api) assert bugbug_utils.should_run_risk_analysis(build)
async def test_dispatch(): """ Test message dispatch from a queue to 2 queues """ bus = MessageBus() bus.add_queue("input") # limit size to immediately stop execution for unit test bus.add_queue("output1", maxsize=3) bus.add_queue("output2", maxsize=3) assert isinstance(bus.queues["input"], asyncio.Queue) assert isinstance(bus.queues["output1"], asyncio.Queue) assert isinstance(bus.queues["output2"], asyncio.Queue) assert bus.queues["input"].qsize() == 0 assert bus.queues["output1"].qsize() == 0 assert bus.queues["output2"].qsize() == 0 await bus.send("input", 1) await bus.send("input", 2) # Convert all strings from input in uppercase assert bus.queues["input"].qsize() == 2 task = asyncio.create_task(bus.dispatch("input", ["output1", "output2"])) await bus.receive("output1") == 1 await bus.receive("output2") == 1 await bus.receive("output1") == 2 await bus.receive("output2") == 2 task.cancel() assert bus.queues["input"].qsize() == 0 assert bus.queues["output1"].qsize() == 0 assert bus.queues["output2"].qsize() == 0
def test_is_coverage_task(mock_taskcluster): bus = MessageBus() hook = CodeCoverage("services-staging-codecoverage/bot", "project-test", bus) cov_task = {"task": {"metadata": {"name": "build-linux64-ccov"}}} assert hook.is_coverage_task(cov_task) cov_task = {"task": {"metadata": {"name": "build-linux1804-64-ccov"}}} assert hook.is_coverage_task(cov_task) cov_task = {"task": {"metadata": {"name": "build-linux64-ccov/opt"}}} assert hook.is_coverage_task(cov_task) cov_task = {"task": {"metadata": {"name": "build-win64-ccov/debug"}}} assert hook.is_coverage_task(cov_task) nocov_task = { "task": { "metadata": { "name": "test-linux64-ccov/opt-mochitest-1" } } } assert hook.is_coverage_task(nocov_task) nocov_task = { "task": { "metadata": { "name": "test-linux64/opt-mochitest-1" } } } assert not hook.is_coverage_task(nocov_task)
async def test_blacklist(PhabricatorMock, mock_taskcluster): bus = MessageBus() build = PhabricatorBuild( MockRequest( diff="123456", repo="PHID-REPO-saax4qdxlbbhahhp2kg5", revision="98765", target="PHID-HMBT-icusvlfibcebizyd33op", ) ) with PhabricatorMock as phab: client = CodeReview( user_blacklist=["baduser123"], url="http://phabricator.test/api/", api_key="fakekey", ) client.register(bus) assert client.user_blacklist == {"PHID-USER-baduser123": "baduser123"} phab.update_state(build) assert build.revision["fields"]["authorPHID"] == "PHID-USER-baduser123" assert client.is_blacklisted(build.revision)
def __init__(self, cache_root): # Create message bus shared amongst processes self.bus = MessageBus() self.workflow = CodeReview( api_key=taskcluster_config.secrets["PHABRICATOR"]["api_key"], url=taskcluster_config.secrets["PHABRICATOR"]["url"], publish=taskcluster_config.secrets["PHABRICATOR"].get( "publish", False), risk_analysis_reviewers=taskcluster_config.secrets.get( "risk_analysis_reviewers", []), community_config=taskcluster_config.secrets.get( "taskcluster_community"), ) self.workflow.register(self.bus) # Build mercurial worker and queue self.mercurial = MercurialWorker( QUEUE_MERCURIAL, QUEUE_PHABRICATOR_RESULTS, repositories=self.workflow.get_repositories( taskcluster_config.secrets["repositories"], cache_root), ) self.mercurial.register(self.bus) # Create web server self.webserver = WebServer(QUEUE_WEB_BUILDS) self.webserver.register(self.bus) # Setup monitoring for newly created tasks self.monitoring = Monitoring(QUEUE_MONITORING, taskcluster_config.secrets["admins"], MONITORING_PERIOD) self.monitoring.register(self.bus)
async def test_start_test_selection(PhabricatorMock, mock_taskcluster): bus = MessageBus() bus.add_queue(QUEUE_MONITORING_COMMUNITY) build = PhabricatorBuild( MockRequest( diff="125397", repo="PHID-REPO-saax4qdxlbbhahhp2kg5", revision="36474", target="PHID-HMBT-icusvlfibcebizyd33op", )) taskcluster_config.secrets["test_selection_enabled"] = True taskcluster_config.secrets["test_selection_share"] = 1.0 with PhabricatorMock as phab: phab.load_patches_stack(build) phab.update_state(build) phab.load_reviewers(build) bugbug_utils = BugbugUtils(phab.api) bugbug_utils.register(bus) with responses.RequestsMock(assert_all_requests_are_fired=True) as rsps: def trigger_hook_callback(request): payload = json.loads(request.body) assert payload == { "DIFF_ID": 125397, "PHABRICATOR_DEPLOYMENT": "prod", "RUNNABLE_JOBS": "http://taskcluster.test/api/index/v1/task/gecko.v2.try.revision.MyRevision.firefox.decision/artifacts/public%2Frunnable-jobs.json", } return ( 200, { "Content-Type": "application/json" }, json.dumps({"status": { "taskId": "xxx" }}), ) rsps.add_callback( responses.POST, "http://community_taskcluster.test/api/hooks/v1/hooks/project-relman/bugbug-test-select/trigger", callback=trigger_hook_callback, ) await bugbug_utils.start_test_selection(build, "MyRevision") group_id, hook_id, task_id = await bus.receive( QUEUE_MONITORING_COMMUNITY) assert group_id == "project-relman" assert hook_id == "bugbug-test-select" assert task_id == "xxx"
async def test_get_coverage_task_in_group(mock_taskcluster): bus = MessageBus() hook = CodeCoverage("services-staging-codecoverage/bot", "project-test", bus) hook.triggered_groups.add("already-triggered-group") assert await hook.get_coverage_task_in_group("already-triggered-group" ) is None
async def test_failure_general(PhabricatorMock, mock_mc): """ Run mercurial worker on a single diff and check the treeherder link publication as an artifact Use a Python common exception to trigger a broken build """ diff = { "phid": "PHID-DIFF-test123", "id": 1234, "baseRevision": None, "revisionPHID": "PHID-DREV-deadbeef", } build = MockBuild(1234, "PHID-REPO-mc", 5678, "PHID-somehash", diff) with PhabricatorMock as phab: phab.load_patches_stack(build) bus = MessageBus() bus.add_queue("phabricator") # Get initial tip commit in repo initial = mock_mc.repo.tip() # The patched and config files should not exist at first repo_dir = mock_mc.repo.root().decode("utf-8") config = os.path.join(repo_dir, "try_task_config.json") target = os.path.join(repo_dir, "test.txt") assert not os.path.exists(target) assert not os.path.exists(config) # Raise an exception during the workflow to trigger a broken build def boom(*args): raise Exception("Boom") mock_mc.apply_build = boom worker = MercurialWorker( "mercurial", "phabricator", repositories={"PHID-REPO-mc": mock_mc} ) worker.register(bus) assert len(worker.repositories) == 1 await bus.send("mercurial", build) assert bus.queues["mercurial"].qsize() == 1 task = asyncio.create_task(worker.run()) # Check the unit result was published mode, out_build, details = await bus.receive("phabricator") assert mode == "fail:general" assert out_build == build assert details["duration"] > 0 assert details["message"] == "Boom" task.cancel() # Clone should not be modified tip = mock_mc.repo.tip() assert tip.node == initial.node
def test_hook_group(mock_taskcluster): bus = MessageBus() hook = CodeCoverage("services-staging-codecoverage/bot", "project-test", bus) assert hook.group_id == "project-test" assert hook.hook_id == "services-staging-codecoverage/bot" hook = CodeCoverage("anotherHook", "anotherProject", bus) assert hook.group_id == "anotherProject" assert hook.hook_id == "anotherHook"
async def test_parse(mock_taskcluster): bus = MessageBus() hook = CodeCoverage("services-staging-codecoverage/bot", "project-test", bus) hook.triggered_groups.add("already-triggered-group") assert (await hook.parse({ "schedulerId": "gecko-level-1", "taskGroupId": "already-triggered-group" }) is None)
async def test_treeherder_link(PhabricatorMock, mock_mc): """ Run mercurial worker on a single diff and check the treeherder link publication as an artifact """ # Preload the build diff = { "phid": "PHID-DIFF-test123", "revisionPHID": "PHID-DREV-deadbeef", "id": 1234, "baseRevision": "abcdef12345", } build = MockBuild(1234, "PHID-REPO-mc", 5678, "PHID-HMBT-somehash", diff) with PhabricatorMock as phab: phab.load_patches_stack(build) bus = MessageBus() bus.add_queue("phabricator") # Get initial tip commit in repo initial = mock_mc.repo.tip() # The patched and config files should not exist at first repo_dir = mock_mc.repo.root().decode("utf-8") config = os.path.join(repo_dir, "try_task_config.json") target = os.path.join(repo_dir, "test.txt") assert not os.path.exists(target) assert not os.path.exists(config) worker = MercurialWorker( "mercurial", "phabricator", repositories={"PHID-REPO-mc": mock_mc} ) worker.register(bus) assert len(worker.repositories) == 1 await bus.send("mercurial", build) assert bus.queues["mercurial"].qsize() == 1 task = asyncio.create_task(worker.run()) # Check the treeherder link was queued mode, out_build, details = await bus.receive("phabricator") tip = mock_mc.repo.tip() assert mode == "success" assert out_build == build assert details[ "treeherder_url" ] == "https://treeherder.mozilla.org/#/jobs?repo=try&revision={}".format( tip.node.decode("utf-8") ) assert details["revision"] == tip.node.decode("utf-8") task.cancel() # Tip should be updated assert tip.node != initial.node
def test_success_windows(mock_taskcluster): bus = MessageBus() hook = CodeCoverage("services-staging-codecoverage/bot", "project-test", bus) assert run_async_parser(hook, "MibGDsa4Q7uFNzDf7EV6nw", "gecko-level-2") == [{ "REPOSITORY": "https://hg.mozilla.org/mozilla-central", "REVISION": "63519bfd42ee379f597c0357af2e712ec3cd9f50", }]
def test_success_try(mock_taskcluster): bus = MessageBus() hook = CodeCoverage("services-staging-codecoverage/bot", "project-test", bus) assert run_async_parser(hook, "FG3goVnCQfif8ZEOaM_4IA", "gecko-level-1") == [{ "REPOSITORY": "https://hg.mozilla.org/try", "REVISION": "066cb18ba95a7efe144e729713c429e422d9f95b", }]
def test_success(mock_taskcluster): bus = MessageBus() hook = CodeCoverage("services-staging-codecoverage/bot", "project-test", bus) assert run_async_parser(hook, "RS0UwZahQ_qAcdZzEb_Y9g", "gecko-level-1") == [{ "REPOSITORY": "https://hg.mozilla.org/mozilla-central", "REVISION": "ec3dd3ee2ae4b3a63529a912816a110e925eb2d0", }]
async def test_failure_mercurial(PhabricatorMock, mock_mc): """ Run mercurial worker on a single diff and check the treeherder link publication as an artifact Apply a bad mercurial patch to trigger a mercurial fail """ diff = { "revisionPHID": "PHID-DREV-666", "baseRevision": "missing", "phid": "PHID-DIFF-666", "id": 666, } build = MockBuild(1234, "PHID-REPO-mc", 5678, "PHID-build-666", diff) with PhabricatorMock as phab: phab.load_patches_stack(build) bus = MessageBus() bus.add_queue("phabricator") # Get initial tip commit in repo initial = mock_mc.repo.tip() # The patched and config files should not exist at first repo_dir = mock_mc.repo.root().decode("utf-8") config = os.path.join(repo_dir, "try_task_config.json") target = os.path.join(repo_dir, "test.txt") assert not os.path.exists(target) assert not os.path.exists(config) worker = MercurialWorker( "mercurial", "phabricator", repositories={"PHID-REPO-mc": mock_mc} ) worker.register(bus) assert len(worker.repositories) == 1 await bus.send("mercurial", build) assert bus.queues["mercurial"].qsize() == 1 task = asyncio.create_task(worker.run()) # Check the treeherder link was queued mode, out_build, details = await bus.receive("phabricator") assert mode == "fail:mercurial" assert out_build == build assert details["duration"] > 0 assert details["message"] == MERCURIAL_FAILURE task.cancel() # Clone should not be modified tip = mock_mc.repo.tip() assert tip.node == initial.node
def __init__(self, cache_root): # Create message bus shared amongst processes self.bus = MessageBus() self.workflow = CodeReview( api_key=taskcluster_config.secrets["PHABRICATOR"]["api_key"], url=taskcluster_config.secrets["PHABRICATOR"]["url"], publish=taskcluster_config.secrets["PHABRICATOR"].get( "publish", False), risk_analysis_reviewers=taskcluster_config.secrets.get( "risk_analysis_reviewers", []), community_config=taskcluster_config.secrets.get( "taskcluster_community"), user_blacklist=taskcluster_config.secrets["user_blacklist"], ) self.workflow.register(self.bus) # Build mercurial worker and queue self.mercurial = MercurialWorker( QUEUE_MERCURIAL, QUEUE_PHABRICATOR_RESULTS, repositories=self.workflow.get_repositories( taskcluster_config.secrets["repositories"], cache_root), ) self.mercurial.register(self.bus) # Create web server self.webserver = WebServer(QUEUE_WEB_BUILDS) self.webserver.register(self.bus) # Setup monitoring for newly created tasks self.monitoring = Monitoring(QUEUE_MONITORING, taskcluster_config.secrets["admins"], MONITORING_PERIOD) self.monitoring.register(self.bus) # Create pulse listener for unit test failures if self.workflow.publish: self.pulse = PulseListener( QUEUE_PULSE, "exchange/taskcluster-queue/v1/task-completed", "*.*.gecko-level-3._", taskcluster_config.secrets["pulse_user"], taskcluster_config.secrets["pulse_password"], ) self.pulse.register(self.bus)
async def test_report_all_completed(QueueMock, NotifyMock, mock_taskcluster): bus = MessageBus() monitoring = Monitoring(mock_taskcluster, "testqueue", ["pinco@pallino"], 1) monitoring.register(bus) await bus.send("testqueue", ("Group1", "Hook1", "Task1-completed")) await bus.send("testqueue", ("Group1", "Hook1", "Task2-completed")) assert bus.queues["testqueue"].qsize() == 2 monitoring.queue = QueueMock monitoring.notify = NotifyMock await monitoring.check_task() await monitoring.check_task() # No email sent, since all tasks were successful. await monitoring.send_report() assert NotifyMock.email_obj == {} assert monitoring.stats == {}
async def test_maxsize(): """ Test a queue maxsize behaves as expected Maxsize=-1 is enabled by default """ bus = MessageBus() bus.add_queue("async") bus.add_queue("mp", mp=True) assert bus.queues["async"].maxsize == -1 # No maxsize getter on mp queues assert bus.queues["async"].empty() assert bus.queues["mp"].empty() for i in range(1000): await bus.send("async", i) await bus.send("mp", i) assert not bus.queues["async"].full() assert not bus.queues["mp"].full()
async def test_message_passing_mp(): """ Test sending & receiving messages on a multiprocessing queueu """ bus = MessageBus() bus.add_queue("test", mp=True) assert isinstance(bus.queues["test"], multiprocessing.queues.Queue) await bus.send("test", {"payload": 1234}) await bus.send("test", {"another": "deadbeef"}) await bus.send("test", "covfefe") assert bus.queues["test"].qsize() == 3 msg = await bus.receive("test") assert msg == {"payload": 1234} msg = await bus.receive("test") assert msg == {"another": "deadbeef"} msg = await bus.receive("test") assert msg == "covfefe" assert bus.queues["test"].qsize() == 0
async def test_run_async_parallel(): """ Test using run to get messages from a queue using an async function executed in parallel """ bus = MessageBus() bus.add_queue("input") bus.add_queue("end", maxsize=1) assert isinstance(bus.queues["input"], asyncio.Queue) assert bus.queues["input"].qsize() == 0 await bus.send("input", 0) await bus.send("input", 1) await bus.send("input", 2) assert bus.queues["input"].qsize() == 3 done = {0: False, 1: False, 2: False} async def torun(count): await asyncio.sleep(0) if count == 0: # Wait for 7 seconds, in the meantime other tasks will be scheduled # and executed. await asyncio.sleep(7) elif count == 1: pass elif count == 2: await bus.send("end", count) else: assert False done[count] = True task = asyncio.create_task(bus.run(torun, "input", sequential=False)) await bus.receive("end") == 1 task.cancel() assert bus.queues["input"].qsize() == 0 assert bus.queues["end"].qsize() == 0 assert done == {0: False, 1: True, 2: True}
async def test_run_async_without_output_queue(): """ Test using run to get messages from a queue using an async function and without an output queue """ bus = MessageBus() bus.add_queue("input") bus.add_queue("end", maxsize=1) assert isinstance(bus.queues["input"], asyncio.Queue) assert bus.queues["input"].qsize() == 0 await bus.send("input", "test x") await bus.send("input", "hello world.") assert bus.queues["input"].qsize() == 2 count = 0 async def torun(payload): nonlocal count if count == 0: assert payload == "test x" elif count == 1: assert payload == "hello world." await bus.send("end", count) else: assert False count += 1 task = asyncio.create_task(bus.run(torun, "input")) await bus.receive("end") == 1 task.cancel() assert bus.queues["input"].qsize() == 0 assert bus.queues["end"].qsize() == 0
def test_queue_creation(): """ Test queues creation with different types """ bus = MessageBus() assert len(bus.queues) == 0 bus.add_queue("test") assert len(bus.queues) == 1 with pytest.raises(AssertionError) as e: bus.add_queue("test") assert str(e.value) == "Queue test already setup" assert len(bus.queues) == 1 bus.add_queue("another") assert len(bus.queues) == 2 bus.add_queue("different", mp=True) assert len(bus.queues) == 3 assert isinstance(bus.queues["test"], asyncio.Queue) assert isinstance(bus.queues["another"], asyncio.Queue) assert isinstance(bus.queues["different"], multiprocessing.queues.Queue)
async def test_got_try_task_end(PhabricatorMock, mock_taskcluster): bus = MessageBus() build = PhabricatorBuild( MockRequest( diff="125397", repo="PHID-REPO-saax4qdxlbbhahhp2kg5", revision="36474", target="PHID-HMBT-icusvlfibcebizyd33op", )) payload = { "routing": { "exchange": "exchange/taskcluster-queue/v1/task-completed", "key": "primary.fUAKaIdkSF6K1NlOgx7-LA.0.aws.i-0a45c84b1709af6a7.gecko-t.t-win10-64.gecko-level-1.RHY-YSgBQ7KlTAaQ5ZWP5g._", "other_routes": [ b"route.tc-treeherder.v2.try.028980a035fb3e214f7645675a01a52234aad0fe.455891" ], }, "body": { "status": { "taskId": "W2SMZ3bYTeanBq-WNpUeHA", "provisionerId": "gecko-t", "workerType": "t-linux-xlarge", "schedulerId": "gecko-level-1", "taskGroupId": "HDnvYOibTMS8h_5Qzv6fWg", "deadline": "2019-11-23T14:04:41.581Z", "expires": "2019-12-06T14:04:41.581Z", "retriesLeft": 5, "state": "completed", "runs": [{ "runId": 0, "state": "completed", "reasonCreated": "scheduled", "reasonResolved": "completed", "workerGroup": "aws", "workerId": "i-01a6b2a05e2211f7c", "takenUntil": "2019-11-22T15:51:57.083Z", "scheduled": "2019-11-22T15:31:56.661Z", "started": "2019-11-22T15:31:57.162Z", "resolved": "2019-11-22T15:42:46.684Z", }], }, "runId": 0, "task": { "tags": { "kind": "test", "worker-implementation": "docker-worker", "createdForUser": "******", "retrigger": "true", "label": "test-linux64-shippable/opt-awsy-tp6-e10s", "os": "linux", } }, "workerGroup": "aws", "workerId": "i-01a6b2a05e2211f7c", "version": 1, }, } taskGroupId = payload["body"]["status"]["taskGroupId"] taskcluster_config.secrets["test_selection_enabled"] = True taskcluster_config.secrets["test_selection_share"] = 0.1 with PhabricatorMock as phab: bus.add_queue(QUEUE_PHABRICATOR_RESULTS) phab.load_patches_stack(build) phab.update_state(build) phab.load_reviewers(build) bugbug_utils = BugbugUtils(phab.api) bugbug_utils.register(bus) try: await bugbug_utils.task_group_to_push.rem(taskGroupId) except KeyError: pass # Nothing happens for tasks that are not test tasks. payload["body"]["task"]["tags"]["kind"] = "source-test" await bugbug_utils.got_try_task_end(payload) assert bus.queues[QUEUE_PHABRICATOR_RESULTS].empty() # Nothing happens for tasks that are not registered. payload["body"]["task"]["tags"]["kind"] = "test" await bugbug_utils.got_try_task_end(payload) assert bus.queues[QUEUE_PHABRICATOR_RESULTS].empty() push = { "build": build, "revision": "028980a035fb3e214f7645675a01a52234aad0fe", } await bugbug_utils.task_group_to_push.set(taskGroupId, push) payload["body"]["status"]["state"] = "completed" await bugbug_utils.got_try_task_end(payload) mode, build_, extras = await bus.receive(QUEUE_PHABRICATOR_RESULTS) assert mode == "test_result" assert build_ == build assert extras == { "name": "test-linux64-shippable/opt-awsy-tp6-e10s", "result": UnitResultState.Pass, "details": None, } payload["body"]["status"]["state"] = "failed" await bugbug_utils.got_try_task_end(payload) mode, build_, extras = await bus.receive(QUEUE_PHABRICATOR_RESULTS) assert mode == "test_result" assert build_ == build assert extras == { "name": "test-linux64-shippable/opt-awsy-tp6-e10s", "result": UnitResultState.Fail, "details": "https://treeherder.mozilla.org/#/jobs?repo=try&revision=028980a035fb3e214f7645675a01a52234aad0fe&selectedTaskRun=W2SMZ3bYTeanBq-WNpUeHA-0", }
async def test_got_bugbug_test_select_end(PhabricatorMock, mock_taskcluster): bus = MessageBus() build = PhabricatorBuild( MockRequest( diff="125397", repo="PHID-REPO-saax4qdxlbbhahhp2kg5", revision="36474", target="PHID-HMBT-icusvlfibcebizyd33op", )) diffId = str(build.diff_id) payload = { "routing": { "exchange": "exchange/taskcluster-queue/v1/task-completed", "key": "primary.OhtlizLqT9ah2jVkUL-yvg.0.community-tc-workers-google.8155538221748661937.proj-relman.compute-large.-.OhtlizLqT9ah2jVkUL-yvg._", "other_routes": [ b"[email protected]", b"route.notify.irc-channel.#bugbug.on-failed", b"route.index.project.relman.bugbug.test_select.latest", f"route.index.project.relman.bugbug.test_select.diff.{diffId}". encode(), b"route.project.relman.bugbug.test_select", ], }, "body": { "status": { "taskId": "bugbug-test-select", "provisionerId": "proj-relman", "workerType": "compute-large", "schedulerId": "-", "taskGroupId": "HDnvYOibTMS8h_5Qzv6fWg", "deadline": "2019-11-27T17:03:07.100Z", "expires": "2019-12-27T15:03:07.100Z", "retriesLeft": 5, "state": "completed", "runs": [{ "runId": 0, "state": "completed", "reasonCreated": "scheduled", "reasonResolved": "completed", "workerGroup": "community-tc-workers-google", "workerId": "8155538221748661937", "takenUntil": "2019-11-27T15:25:02.767Z", "scheduled": "2019-11-27T15:03:07.606Z", "started": "2019-11-27T15:05:02.786Z", "resolved": "2019-11-27T15:19:24.809Z", }], }, "runId": 0, "task": { "tags": {} }, "workerGroup": "community-tc-workers-google", "workerId": "8155538221748661937", "version": 1, }, } taskGroupId = payload["body"]["status"]["taskGroupId"] taskcluster_config.secrets["test_selection_enabled"] = True taskcluster_config.secrets["test_selection_share"] = 0.1 with PhabricatorMock as phab: phab.load_patches_stack(build) phab.update_state(build) bugbug_utils = BugbugUtils(phab.api) bugbug_utils.register(bus) try: await bugbug_utils.diff_to_push.rem(diffId) except KeyError: pass try: await bugbug_utils.task_group_to_push.rem(taskGroupId) except KeyError: pass # Nothing happens when diff_to_push is empty. with responses.RequestsMock(assert_all_requests_are_fired=True) as rsps: rsps.add( responses.GET, "http://community_taskcluster.test/api/queue/v1/task/bugbug-test-select", body=mock_taskcluster("task-bugbug-test-select.json"), content_type="application/json", ) rsps.add( responses.GET, "http://community_taskcluster.test/api/queue/v1/task/bugbug-test-select/artifacts/public%2Ffailure_risk", body=mock_taskcluster("artifact-bugbug-test-select-failure-risk"), ) rsps.add( responses.GET, "http://community_taskcluster.test/api/queue/v1/task/bugbug-test-select/artifacts/public%2Fselected_tasks", body=mock_taskcluster( "artifact-bugbug-test-select-selected-tasks"), ) with pytest.raises(KeyError): await bugbug_utils.diff_to_push.get(diffId) await bugbug_utils.got_bugbug_test_select_end(payload) with pytest.raises(KeyError): await bugbug_utils.task_group_to_push.get(taskGroupId) # Nothing happens when we are on the wrong Phabricator deployment. with responses.RequestsMock(assert_all_requests_are_fired=True) as rsps: rsps.add( responses.GET, "http://community_taskcluster.test/api/queue/v1/task/bugbug-test-select", body=mock_taskcluster("task-bugbug-test-select.json"), content_type="application/json", ) bugbug_utils.phabricator_deployment = "dev" await bugbug_utils.diff_to_push.set(diffId, { "revision": "123", "build": build }) await bugbug_utils.got_bugbug_test_select_end(payload) with pytest.raises(KeyError): await bugbug_utils.task_group_to_push.get(taskGroupId) await bugbug_utils.diff_to_push.rem(diffId) bugbug_utils.phabricator_deployment = "prod" # Nothing happens when the failure risk is low. with responses.RequestsMock(assert_all_requests_are_fired=True) as rsps: rsps.add( responses.GET, "http://community_taskcluster.test/api/queue/v1/task/bugbug-test-select", body=mock_taskcluster("task-bugbug-test-select.json"), content_type="application/json", ) rsps.add( responses.GET, "http://community_taskcluster.test/api/queue/v1/task/bugbug-test-select/artifacts/public%2Ffailure_risk", body=mock_taskcluster( "artifact-bugbug-test-select-failure-risk-0"), ) await bugbug_utils.diff_to_push.set(diffId, { "revision": "123", "build": build }) await bugbug_utils.got_bugbug_test_select_end(payload) with pytest.raises(KeyError): await bugbug_utils.task_group_to_push.get(taskGroupId) # Wait removal of object from diff_to_push to be done. tasks = set(asyncio.all_tasks()) tasks.remove(asyncio.current_task()) await asyncio.gather(*tasks) with pytest.raises(KeyError): await bugbug_utils.diff_to_push.get(diffId) # Nothing happens when the failure risk is high but there are no selected tasks. with responses.RequestsMock(assert_all_requests_are_fired=True) as rsps: rsps.add( responses.GET, "http://community_taskcluster.test/api/queue/v1/task/bugbug-test-select", body=mock_taskcluster("task-bugbug-test-select.json"), content_type="application/json", ) rsps.add( responses.GET, "http://community_taskcluster.test/api/queue/v1/task/bugbug-test-select/artifacts/public%2Ffailure_risk", body=mock_taskcluster("artifact-bugbug-test-select-failure-risk"), ) rsps.add( responses.GET, "http://community_taskcluster.test/api/queue/v1/task/bugbug-test-select/artifacts/public%2Fselected_tasks", body=mock_taskcluster( "artifact-bugbug-test-select-selected-tasks-none"), ) await bugbug_utils.diff_to_push.set(diffId, { "revision": "123", "build": build }) await bugbug_utils.got_bugbug_test_select_end(payload) with pytest.raises(KeyError): await bugbug_utils.task_group_to_push.get(taskGroupId) # Wait removal of object from diff_to_push to be done. tasks = set(asyncio.all_tasks()) tasks.remove(asyncio.current_task()) await asyncio.gather(*tasks) with pytest.raises(KeyError): await bugbug_utils.diff_to_push.get(diffId) # Stuff happens. with responses.RequestsMock(assert_all_requests_are_fired=True) as rsps: rsps.add( responses.GET, "http://community_taskcluster.test/api/queue/v1/task/bugbug-test-select", body=mock_taskcluster("task-bugbug-test-select.json"), content_type="application/json", ) rsps.add( responses.GET, "http://community_taskcluster.test/api/queue/v1/task/bugbug-test-select/artifacts/public%2Ffailure_risk", body=mock_taskcluster("artifact-bugbug-test-select-failure-risk"), ) rsps.add( responses.GET, "http://community_taskcluster.test/api/queue/v1/task/bugbug-test-select/artifacts/public%2Fselected_tasks", body=mock_taskcluster( "artifact-bugbug-test-select-selected-tasks"), ) rsps.add( responses.GET, "http://taskcluster.test/api/index/v1/task/gecko.v2.try.revision.123.taskgraph.decision", body=mock_taskcluster("index-gecko-decision.json"), content_type="application/json", ) rsps.add( responses.GET, f"http://taskcluster.test/api/queue/v1/task/{taskGroupId}/artifacts/public%2Factions.json", body=mock_taskcluster("artifact-gecko-decision-actions.json"), content_type="application/json", ) def trigger_hook_callback(request): payload = json.loads(request.body) assert payload == json.loads( mock_taskcluster("trigger-hook-add-new-jobs-post-body.json")) return ( 200, { "Content-Type": "application/json" }, mock_taskcluster("trigger-hook-add-new-jobs.json"), ) rsps.add_callback( responses.POST, "http://taskcluster.test/api/hooks/v1/hooks/project-gecko/in-tree-action-1-generic%2Fea5d85cbef/trigger", callback=trigger_hook_callback, ) push = {"revision": "123", "build": build} await bugbug_utils.diff_to_push.set(diffId, push) await bugbug_utils.got_bugbug_test_select_end(payload) assert await bugbug_utils.task_group_to_push.get(taskGroupId) == push # Wait removal of object from diff_to_push to be done. tasks = set(asyncio.all_tasks()) tasks.remove(asyncio.current_task()) await asyncio.gather(*tasks) with pytest.raises(KeyError): await bugbug_utils.diff_to_push.get(diffId)