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
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
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'))
    task.cancel()

    # Tip should be updated
    assert tip.node != initial.node
Example #4
0
async def test_monitoring_retry_exceptions(QueueMock, NotifyMock,
                                           mock_taskcluster):
    bus = MessageBus()
    monitoring = Monitoring('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 = 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
Example #5
0
async def test_parse(mock_taskcluster):
    bus = MessageBus()
    hook = CodeCoverage({'hookId': 'services-staging-codecoverage/bot'}, bus)

    hook.triggered_groups.add('already-triggered-group')

    assert await hook.parse({'taskGroupId': 'already-triggered-group'}) is None
Example #6
0
def test_is_coverage_task(mock_taskcluster):
    bus = MessageBus()
    hook = CodeCoverage({'hookId': 'services-staging-codecoverage/bot'}, bus)

    cov_task = {'task': {'metadata': {'name': 'build-linux64-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 not hook.is_coverage_task(nocov_task)

    nocov_task = {
        'task': {
            'metadata': {
                'name': 'test-linux64/opt-mochitest-1'
            }
        }
    }
    assert not hook.is_coverage_task(nocov_task)
Example #7
0
async def test_monitoring_restartable(QueueMock, IndexMock, mock_taskcluster):
    bus = MessageBus()
    monitoring = Monitoring('testqueue', ['pinco@pallino'], 1)
    monitoring.register(bus)

    monitoring.index = IndexMock
    monitoring.queue = QueueMock

    # Check a failed task is restartable
    # when the monitoring flag is set
    assert monitoring.is_restartable('Task-failed-restart')

    # Check a failed task is not restartable
    # when the monitoring flag is not set
    assert not monitoring.is_restartable('Task-failed-nope')

    # Check a completed task is not restartable
    assert not monitoring.is_restartable('Task-completed-restart')
    assert not monitoring.is_restartable('Task-completed-nope')

    # Check a failed task is restarted by the monitoring flow
    assert len(monitoring.queue.created_tasks) == 0
    await bus.send('testqueue',
                   ('Group', 'Hook-staticanalysis/bot', 'Task-failed-restart'))
    await monitoring.check_task()
    assert len(monitoring.queue.created_tasks) == 1
Example #8
0
async def test_get_build_task_in_group(mock_taskcluster):
    bus = MessageBus()
    hook = CodeCoverage({'hookId': 'services-staging-codecoverage/bot'}, bus)

    hook.triggered_groups.add('already-triggered-group')

    assert await hook.get_build_task_in_group('already-triggered-group'
                                              ) is None
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
Example #10
0
def test_success(mock_taskcluster):
    bus = MessageBus()
    hook = CodeCoverage({'hookId': 'services-staging-codecoverage/bot'}, bus)

    assert run_async_parser(hook, 'RS0UwZahQ_qAcdZzEb_Y9g') == [{
        'REPOSITORY':
        'https://hg.mozilla.org/mozilla-central',
        'REVISION':
        'ec3dd3ee2ae4b3a63529a912816a110e925eb2d0'
    }]
Example #11
0
def test_success_windows(mock_taskcluster):
    bus = MessageBus()
    hook = CodeCoverage({'hookId': 'services-staging-codecoverage/bot'}, bus)

    assert run_async_parser(hook, 'MibGDsa4Q7uFNzDf7EV6nw') == [{
        'REPOSITORY':
        'https://hg.mozilla.org/mozilla-central',
        'REVISION':
        '63519bfd42ee379f597c0357af2e712ec3cd9f50'
    }]
Example #12
0
def test_success_try(mock_taskcluster):
    bus = MessageBus()
    hook = CodeCoverage({'hookId': 'services-staging-codecoverage/bot'}, bus)

    assert run_async_parser(hook, 'FG3goVnCQfif8ZEOaM_4IA') == [{
        'REPOSITORY':
        'https://hg.mozilla.org/try',
        'REVISION':
        '066cb18ba95a7efe144e729713c429e422d9f95b'
    }]
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
def test_success_windows(mock_taskcluster):
    bus = MessageBus()
    with open(os.path.join(FIXTURES_DIR, 'MibGDsa4Q7uFNzDf7EV6nw.json')) as f:
        responses.add(responses.GET, 'http://taskcluster.test/queue/v1/task-group/MibGDsa4Q7uFNzDf7EV6nw/list', json=json.load(f), status=200, match_querystring=True)  # noqa

    hook = CodeCoverage({
      'hookId': 'services-staging-codecoverage/bot'
    }, bus)

    assert hook.parse({
        'taskGroupId': 'MibGDsa4Q7uFNzDf7EV6nw'
    }) == [{'REPOSITORY': 'https://hg.mozilla.org/mozilla-central', 'REVISION': '63519bfd42ee379f597c0357af2e712ec3cd9f50'}]
def test_wrong_branch(mock_taskcluster):
    bus = MessageBus()
    with open(os.path.join(FIXTURES_DIR, 'bNq-VIT-Q12o6nXcaUmYNQ.json')) as f:
        responses.add(responses.GET, 'http://taskcluster.test/queue/v1/task-group/bNq-VIT-Q12o6nXcaUmYNQ/list', json=json.load(f), status=200, match_querystring=True)  # noqa

    hook = CodeCoverage({
      'hookId': 'services-staging-codecoverage/bot'
    }, bus)

    assert hook.parse({
        'taskGroupId': 'bNq-VIT-Q12o6nXcaUmYNQ'
    }) is None
def test_success(mock_taskcluster):
    bus = MessageBus()
    with open(os.path.join(FIXTURES_DIR, 'RS0UwZahQ_qAcdZzEb_Y9g.json')) as f:
        responses.add(responses.GET, 'http://taskcluster.test/queue/v1/task-group/RS0UwZahQ_qAcdZzEb_Y9g/list', json=json.load(f), status=200, match_querystring=True)  # noqa

    hook = CodeCoverage({
      'hookId': 'services-staging-codecoverage/bot'
    }, bus)

    assert hook.parse({
        'taskGroupId': 'RS0UwZahQ_qAcdZzEb_Y9g'
    }) == [{'REPOSITORY': 'https://hg.mozilla.org/mozilla-central', 'REVISION': 'ec3dd3ee2ae4b3a63529a912816a110e925eb2d0'}]
def test_success_try(mock_taskcluster):
    bus = MessageBus()
    with open(os.path.join(FIXTURES_DIR, 'FG3goVnCQfif8ZEOaM_4IA.json')) as f:
        responses.add(responses.GET, 'http://taskcluster.test/queue/v1/task-group/FG3goVnCQfif8ZEOaM_4IA/list', json=json.load(f), status=200, match_querystring=True)  # noqa

    hook = CodeCoverage({
      'hookId': 'services-staging-codecoverage/bot'
    }, bus)

    assert hook.parse({
        'taskGroupId': 'FG3goVnCQfif8ZEOaM_4IA'
    }) == [{'REPOSITORY': 'https://hg.mozilla.org/try', 'REVISION': '066cb18ba95a7efe144e729713c429e422d9f95b'}]
Example #18
0
def test_hook_group(mock_taskcluster):
    bus = MessageBus()
    hook = CodeCoverage({'hookId': 'services-staging-codecoverage/bot'}, bus)
    assert hook.group_id == 'project-releng'
    assert hook.hook_id == 'services-staging-codecoverage/bot'

    hook = CodeCoverage(
        {
            'hookGroupId': 'anotherProject',
            'hookId': 'anotherHook',
        }, bus)
    assert hook.group_id == 'anotherProject'
    assert hook.hook_id == 'anotherHook'
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)
Example #20
0
async def test_report_all_completed(QueueMock, NotifyMock, mock_taskcluster):
    bus = MessageBus()
    monitoring = Monitoring('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.
    monitoring.send_report()
    assert NotifyMock.email_obj == {}
    assert monitoring.stats == {}
Example #21
0
async def test_create_task(HooksMock, QueueMock, mock_taskcluster):
    bus = MessageBus()
    bus.add_queue(QUEUE_MONITORING, maxsize=1)
    bus.add_queue(QUEUE_PULSE_CODECOV)

    conf = {
        'hookGroupId': 'aGroup',
        'hookId': 'aHook',
    }
    hook = CodeCoverage(conf, bus)
    hook.hooks = HooksMock
    hook.queue = QueueMock

    # Add a dummy task in the target group
    QueueMock.add_task_in_group('aGroup',
                                name='whatever',
                                env={'key': 'value'})

    # Add a coverage task in the target group
    env = {
        'GECKO_HEAD_REPOSITORY': 'https://hg.mozilla.org/mozilla-central',
        'GECKO_HEAD_REV': 'deadbeef',
    }
    QueueMock.add_task_in_group('aGroup',
                                name='build-linux64-ccov/test',
                                env=env)

    # Send a pulse message with the target group
    pulse_payload = {
        'taskGroupId': 'aGroup',
    }
    await bus.send(QUEUE_PULSE_CODECOV, pulse_payload)

    # Run the code coverage event listener as a task
    task = asyncio.create_task(hook.run())

    # Stop as soon as a message is sent to monitoring
    group_id, hook_id, task_id = await bus.queues[QUEUE_MONITORING].get()
    task.cancel()
    assert bus.queues[QUEUE_MONITORING].qsize() == 0

    assert group_id == 'aGroup'
    assert hook_id == 'aHook'
    assert task_id == 'fake_task_id'

    assert HooksMock.obj['group_id'] == 'aGroup'
    assert HooksMock.obj['hook_id'] == 'aHook'
    assert HooksMock.obj['payload'] == {
        'REPOSITORY': 'https://hg.mozilla.org/mozilla-central',
        'REVISION': 'deadbeef',
    }
Example #22
0
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:
        client = CodeReview(
            risk_analysis_reviewers=['ehsan', 'heycam'],
            url='http://phabricator.test/api/',
            api_key='fakekey',
        )
        client.register(bus)

        phab.update_state(build)
        phab.load_reviewers(build)

    assert client.should_run_risk_analysis(build)
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()
Example #24
0
async def test_monitoring(QueueMock, NotifyMock, mock_taskcluster):
    bus = MessageBus()
    monitoring = Monitoring('testqueue', ['pinco@pallino'], 1)
    monitoring.register(bus)
    await bus.send('testqueue', ('Group1', 'Hook1', 'Task-invalid'))
    await bus.send('testqueue', ('Group1', 'Hook1', 'Task-pending'))
    await bus.send('testqueue', ('Group1', 'Hook1', 'Task1-completed'))
    await bus.send('testqueue', ('Group1', 'Hook1', 'Task2-completed'))
    await bus.send('testqueue', ('Group1', 'Hook2', 'Task-exception'))
    await bus.send('testqueue', ('Group2', 'Hook1', 'Task-failed'))
    assert bus.queues['testqueue'].qsize() == 6

    monitoring.queue = QueueMock
    monitoring.notify = NotifyMock

    # No report sent, since we haven't collected any stats yet.
    monitoring.send_report()
    assert NotifyMock.email_obj == {}

    # Queue throws exception, remove task from queue.
    await monitoring.check_task()
    assert bus.queues['testqueue'].qsize() == 5

    # Task is pending, put it back in the queue.
    await monitoring.check_task()
    assert bus.queues['testqueue'].qsize() == 5

    # No report sent, since we haven't collected any stats yet.
    monitoring.send_report()
    assert NotifyMock.email_obj == {}

    # Task is completed.
    await monitoring.check_task()
    assert monitoring.stats['Hook1']['completed'] == ['Task1-completed']
    assert bus.queues['testqueue'].qsize() == 4

    # Another task is completed.
    await monitoring.check_task()
    assert monitoring.stats['Hook1']['completed'] == [
        'Task1-completed', 'Task2-completed'
    ]
    assert bus.queues['testqueue'].qsize() == 3

    # Task exception.
    assert len(monitoring.queue.created_tasks) == 0
    await monitoring.check_task()
    assert monitoring.stats['Hook1']['exception'] == []
    assert monitoring.stats['Hook2']['exception'] == ['Task-exception']

    # A new task has been retried, replacing the exception
    assert len(monitoring.queue.created_tasks) == 1
    assert bus.queues['testqueue'].qsize() == 3

    # Task failed.
    await monitoring.check_task()
    assert monitoring.stats['Hook1']['failed'] == ['Task-failed']
    assert bus.queues['testqueue'].qsize() == 2

    # Task is pending, put it back in the queue.
    await monitoring.check_task()
    assert bus.queues['testqueue'].qsize() == 2

    content = '''# Hook1 tasks for the last period


## completed

66.67% of all tasks (2/3)

* [Task1-completed](https://tools.taskcluster.net/task-inspector/#Task1-completed)
* [Task2-completed](https://tools.taskcluster.net/task-inspector/#Task2-completed)

## exception

0.00% of all tasks (0/3)



## failed

33.33% of all tasks (1/3)

* [Task-failed](https://tools.taskcluster.net/task-inspector/#Task-failed)

# Hook2 tasks for the last period


## completed

0.00% of all tasks (0/1)



## exception

100.00% of all tasks (1/1)

* [Task-exception](https://tools.taskcluster.net/task-inspector/#Task-exception)

## failed

0.00% of all tasks (0/1)

'''

    monitoring.send_report()
    assert NotifyMock.email_obj['address'] == 'pinco@pallino'
    assert NotifyMock.email_obj['subject'] == 'Pulse listener tasks'
    assert NotifyMock.email_obj['content'] == content
    assert NotifyMock.email_obj['template'] == 'fullscreen'
    assert monitoring.stats == {}
async def test_push_to_try_existing_rev(PhabricatorMock, mock_mc):
    '''
    Run mercurial worker on a single diff
    with a push to try server
    but applying on an existing revision
    '''
    bus = MessageBus()
    bus.add_queue('phabricator')
    repo_dir = mock_mc.repo.root().decode('utf-8')

    def _readme(content):
        # Make a commit on README.md in the repo
        readme = os.path.join(repo_dir, 'README.md')
        with open(readme, 'a') as f:
            f.write(content)
        _, rev = mock_mc.repo.commit(message=content.encode('utf-8'), user=b'test')
        return rev

    # Make two commits, the first one is our base
    base = _readme('Local base for diffs')
    extra = _readme('EXTRA')

    # Preload the build
    diff = {
        'phid': 'PHID-DIFF-solo',
        'revisionPHID': 'PHID-DREV-solo',
        'id': 9876,

        # Revision does not exist, will apply on tip
        'baseRevision': base,
    }
    build = MockBuild(1234, 'PHID-REPO-mc', 5678, 'PHID-HMBT-deadbeef', diff)
    with PhabricatorMock as phab:
        phab.load_patches_stack(build)

    # The patched and config files should not exist at first
    target = os.path.join(repo_dir, 'solo.txt')
    config = os.path.join(repo_dir, 'try_task_config.json')
    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'))
    task.cancel()

    # The target should have content now
    assert os.path.exists(target)
    assert open(target).read() == 'Solo PATCH\n'

    # Check the try_task_config file
    assert os.path.exists(config)
    assert json.load(open(config)) == {
        'version': 2,
        'parameters': {
            'target_tasks_method': 'codereview',
            'optimize_target_tasks': True,
            'phabricator_diff': 'PHID-HMBT-deadbeef',
        }
    }

    # Get tip commit in repo
    # It should be different from the initial one (patches and config have applied)
    assert tip.node != base
    assert tip.desc == b'try_task_config for code-review\nDifferential Diff: PHID-DIFF-solo'

    # Check the push to try has been called
    # with tip commit
    ssh_conf = 'ssh -o StrictHostKeyChecking="no" -o User="******" -o IdentityFile="{}"'.format(mock_mc.ssh_key_path)
    mock_mc.repo.push.assert_called_with(
        dest=b'http://mozilla-central/try',
        force=True,
        rev=tip.node,
        ssh=ssh_conf.encode('utf-8'),
    )

    # Check the parent is the solo patch commit
    parents = mock_mc.repo.parents(tip.node)
    assert len(parents) == 1
    parent = parents[0]
    assert parent.desc == b'A nice human readable commit message\nDifferential Diff: PHID-DIFF-solo'

    # Check the grand parent is the base, not extra
    great_parents = mock_mc.repo.parents(parent.node)
    assert len(great_parents) == 1
    great_parent = great_parents[0]
    assert great_parent.node == base

    # Extra commit should not appear
    assert parent.node != extra
    assert great_parent.node != extra
    assert 'EXTRA' not in open(os.path.join(repo_dir, 'README.md')).read()
Example #26
0
def test_wrong_branch(mock_taskcluster):
    bus = MessageBus()
    hook = CodeCoverage({'hookId': 'services-staging-codecoverage/bot'}, bus)

    assert run_async_parser(hook, 'bNq-VIT-Q12o6nXcaUmYNQ') is None
Example #27
0
async def test_monitoring_whiteline_between_failed_and_hook(
        QueueMock, NotifyMock, mock_taskcluster):
    bus = MessageBus()
    monitoring = Monitoring('testqueue', ['pinco@pallino'], 1)
    monitoring.register(bus)
    await bus.send('testqueue', ('Group1', 'Hook1', 'Task-failed'))
    await bus.send('testqueue', ('Group1', 'Hook2', 'Task-failed'))
    assert bus.queues['testqueue'].qsize() == 2

    monitoring.queue = QueueMock
    monitoring.notify = NotifyMock

    # Task exception.
    await monitoring.check_task()
    assert monitoring.stats['Hook1']['failed'] == ['Task-failed']
    assert bus.queues['testqueue'].qsize() == 1

    # Task failed.
    await monitoring.check_task()
    assert monitoring.stats['Hook2']['failed'] == ['Task-failed']
    assert bus.queues['testqueue'].qsize() == 0

    content = '''# Hook1 tasks for the last period


## completed

0.00% of all tasks (0/1)



## exception

0.00% of all tasks (0/1)



## failed

100.00% of all tasks (1/1)

* [Task-failed](https://tools.taskcluster.net/task-inspector/#Task-failed)

# Hook2 tasks for the last period


## completed

0.00% of all tasks (0/1)



## exception

0.00% of all tasks (0/1)



## failed

100.00% of all tasks (1/1)

* [Task-failed](https://tools.taskcluster.net/task-inspector/#Task-failed)'''

    monitoring.send_report()
    assert NotifyMock.email_obj['address'] == 'pinco@pallino'
    assert NotifyMock.email_obj['subject'] == 'Pulse listener tasks'
    assert NotifyMock.email_obj['content'] == content
    assert NotifyMock.email_obj['template'] == 'fullscreen'
    assert monitoring.stats == {}
Example #28
0
    def __init__(self, cache_root):
        # Create message bus shared amongst process
        self.bus = MessageBus()

        # Build client applications configuration
        # TODO: use simpler secret structure per client
        clients_conf = {h['type']: h for h in taskcluster.secrets['HOOKS']}
        code_review_conf = clients_conf.get('static-analysis-phabricator')
        code_coverage_conf = clients_conf.get('code-coverage')

        # Code Review Workflow
        if code_review_conf:
            self.code_review = CodeReview(
                api_key=taskcluster.secrets['PHABRICATOR']['token'],
                url=taskcluster.secrets['PHABRICATOR']['url'],
                publish=taskcluster.secrets['PHABRICATOR'].get(
                    'publish', False),
                risk_analysis_reviewers=code_review_conf.get(
                    'risk_analysis_reviewers', []))
            self.code_review.register(self.bus)

            # Build mercurial worker & queue
            self.mercurial = MercurialWorker(
                QUEUE_MERCURIAL,
                QUEUE_PHABRICATOR_RESULTS,
                repositories=self.code_review.get_repositories(
                    taskcluster.secrets['repositories'], cache_root),
            )
            self.mercurial.register(self.bus)

            # Create web server
            self.webserver = WebServer(QUEUE_WEB_BUILDS)
            self.webserver.register(self.bus)
        else:
            self.code_review = None
            self.mercurial = None
            self.webserver = None

        # Code Coverage Workflow
        if code_coverage_conf:
            self.code_coverage = CodeCoverage(code_coverage_conf, self.bus)

            # Setup monitoring for newly created tasks
            self.monitoring = Monitoring(QUEUE_MONITORING,
                                         taskcluster.secrets['ADMINS'],
                                         7 * 3600)
            self.monitoring.register(self.bus)

            # Create pulse listener for code coverage
            self.pulse = PulseListener(
                QUEUE_PULSE_CODECOV,
                'exchange/taskcluster-queue/v1/task-group-resolved',
                '#',
                taskcluster.secrets['PULSE_USER'],
                taskcluster.secrets['PULSE_PASSWORD'],
            )
            self.pulse.register(self.bus)

        else:
            self.code_coverage = None
            self.monitoring = None
            self.pulse = None

        assert self.code_review or self.code_coverage, 'No client applications to run !'
Example #29
0
class EventListener(object):
    '''
    Listen to external events and trigger new tasks
    '''
    def __init__(self, cache_root):
        # Create message bus shared amongst process
        self.bus = MessageBus()

        # Build client applications configuration
        # TODO: use simpler secret structure per client
        clients_conf = {h['type']: h for h in taskcluster.secrets['HOOKS']}
        code_review_conf = clients_conf.get('static-analysis-phabricator')
        code_coverage_conf = clients_conf.get('code-coverage')

        # Code Review Workflow
        if code_review_conf:
            self.code_review = CodeReview(
                api_key=taskcluster.secrets['PHABRICATOR']['token'],
                url=taskcluster.secrets['PHABRICATOR']['url'],
                publish=taskcluster.secrets['PHABRICATOR'].get(
                    'publish', False),
                risk_analysis_reviewers=code_review_conf.get(
                    'risk_analysis_reviewers', []))
            self.code_review.register(self.bus)

            # Build mercurial worker & queue
            self.mercurial = MercurialWorker(
                QUEUE_MERCURIAL,
                QUEUE_PHABRICATOR_RESULTS,
                repositories=self.code_review.get_repositories(
                    taskcluster.secrets['repositories'], cache_root),
            )
            self.mercurial.register(self.bus)

            # Create web server
            self.webserver = WebServer(QUEUE_WEB_BUILDS)
            self.webserver.register(self.bus)
        else:
            self.code_review = None
            self.mercurial = None
            self.webserver = None

        # Code Coverage Workflow
        if code_coverage_conf:
            self.code_coverage = CodeCoverage(code_coverage_conf, self.bus)

            # Setup monitoring for newly created tasks
            self.monitoring = Monitoring(QUEUE_MONITORING,
                                         taskcluster.secrets['ADMINS'],
                                         7 * 3600)
            self.monitoring.register(self.bus)

            # Create pulse listener for code coverage
            self.pulse = PulseListener(
                QUEUE_PULSE_CODECOV,
                'exchange/taskcluster-queue/v1/task-group-resolved',
                '#',
                taskcluster.secrets['PULSE_USER'],
                taskcluster.secrets['PULSE_PASSWORD'],
            )
            self.pulse.register(self.bus)

        else:
            self.code_coverage = None
            self.monitoring = None
            self.pulse = None

        assert self.code_review or self.code_coverage, 'No client applications to run !'

    def run(self):
        consumers = []

        if self.code_review:
            consumers += [
                # Code review main workflow
                self.code_review.run(),

                # Add mercurial task
                self.mercurial.run(),
            ]

            # Publish results on Phabricator
            if self.code_review.publish:
                consumers.append(
                    self.bus.run(self.code_review.publish_results,
                                 QUEUE_PHABRICATOR_RESULTS))

            # Start the web server in its own process
            self.webserver.start()

        if self.code_coverage:
            consumers += [
                # Code coverage main workflow
                self.code_coverage.run(),

                # Add monitoring task
                self.monitoring.run(),

                # Add pulse task
                self.pulse.run(),
            ]

        # Run all tasks concurrently
        run_tasks(consumers)

        # Stop the webserver when other async process are stopped
        if self.webserver:
            self.webserver.stop()
async def test_push_to_try_nss(PhabricatorMock, mock_nss):
    '''
    Run mercurial worker on a single diff
    with a push to try server, but with NSS support (try syntax)
    '''
    diff = {
        'phid': 'PHID-DIFF-test123',
        'revisionPHID': 'PHID-DREV-deadbeef',
        'id': 1234,

        # Revision does not exist, will apply on tip
        'baseRevision': 'abcdef12345',
    }
    build = MockBuild(1234, 'PHID-REPO-nss', 5678, 'PHID-HMBT-deadbeef', diff)
    with PhabricatorMock as phab:
        phab.load_patches_stack(build)

    bus = MessageBus()
    bus.add_queue('phabricator')

    # Get initial tip commit in repo
    initial = mock_nss.repo.tip()

    # The patched and config files should not exist at first
    repo_dir = mock_nss.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-nss': mock_nss}
    )
    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_nss.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'))
    task.cancel()

    # The target should have content now
    assert os.path.exists(target)
    assert open(target).read() == 'First Line\nSecond Line\n'

    # The config should have content now
    assert os.path.exists(config)
    assert json.load(open(config)) == {
        'version': 2,
        'parameters': {
            'code-review': {
                'phabricator-build-target': 'PHID-HMBT-deadbeef',
            }
        },
    }

    # Get tip commit in repo
    # It should be different from the initial one (patches + config have applied)
    assert tip.node != initial.node

    # Check all commits messages
    assert [c.desc for c in mock_nss.repo.log()] == [
        b'try: -a -b XXX -c YYY',
        b'Bug XXX - A second commit message\nDifferential Diff: PHID-DIFF-test123',
        b'Bug XXX - A first commit message\nDifferential Diff: PHID-DIFF-xxxx',
        b'Readme'
    ]

    # Check the push to try has been called
    # with tip commit
    ssh_conf = 'ssh -o StrictHostKeyChecking="no" -o User="******" -o IdentityFile="{}"'.format(mock_nss.ssh_key_path)
    mock_nss.repo.push.assert_called_with(
        dest=b'http://nss/try',
        force=True,
        rev=tip.node,
        ssh=ssh_conf.encode('utf-8'),
    )