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
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
Exemple #4
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',
    }
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
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_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()
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_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'),
    )
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()