Exemplo n.º 1
0
    def _test_reporter(api, analyzers):
        # Always use the same setup, only varies the analyzers
        revision = PhabricatorRevision('PHID-DIFF-abcdef', api)
        revision.lines = {
            'test.cpp': [41, 42, 43],
        }
        reporter = PhabricatorReporter({'analyzers': analyzers}, api=api)

        issues = [
            ClangFormatIssue('test.cpp', 42, 1, revision),
            ClangTidyIssue(('test.cpp', '42', '51', 'error', 'dummy message', 'modernize-use-nullptr'), revision),
            InferIssue({
                'file': 'test.cpp',
                'line': 42,
                'column': 1,
                'bug_type': 'dummy',
                'kind': 'whatever',
                'qualifier': 'dummy message.',
            }, revision),
            MozLintIssue('test.cpp', 1, 'danger', 42, 'flake8', 'Python error', 'EXXX', revision),
        ]

        assert all(i.is_publishable() for i in issues)

        revision.improvement_patches = {
            'dummy': 'not gonna work',
            'clang-format': 'https://diff.url/clang-format',
            'clang-tidy': 'https://diff.url/clang-tidy',
            'mozlint': 'https://diff.url/mozlint',
            'infer': 'https://diff.url/infer',
        }

        return reporter.publish(issues, revision)
Exemplo n.º 2
0
def test_phabricator_clang_format(mock_repository, mock_phabricator):
    '''
    Test Phabricator reporter publication on a mock clang-format issue
    '''
    from static_analysis_bot.report.phabricator import PhabricatorReporter
    from static_analysis_bot.revisions import PhabricatorRevision
    from static_analysis_bot.clang.format import ClangFormatIssue

    def _check_comment(request):
        # Check the Phabricator main comment is well formed
        payload = urllib.parse.parse_qs(request.body)
        assert payload['output'] == ['json']
        assert len(payload['params']) == 1
        details = json.loads(payload['params'][0])
        assert details['message'] == VALID_CLANG_FORMAT_MESSAGE

        # Outputs dummy empty response
        resp = {
            'error_code': None,
            'result': None,
        }
        return 201, {
            'Content-Type': 'application/json',
            'unittest': 'clang-format'
        }, json.dumps(resp)

    responses.add_callback(
        responses.POST,
        'http://phabricator.test/api/differential.createcomment',
        callback=_check_comment,
    )

    with mock_phabricator as api:
        revision = PhabricatorRevision('PHID-DIFF-abcdef', api)
        revision.lines = {
            # Add dummy lines diff
            'test.cpp': [41, 42, 43],
            'dom/test.cpp': [
                42,
            ],
        }
        reporter = PhabricatorReporter({'analyzers': ['clang-format']},
                                       api=api)

    issue = ClangFormatIssue('dom/test.cpp', 42, 1, revision)
    assert issue.is_publishable()

    revision.improvement_patches = {'clang-format': 'https://diff.url'}

    issues, patches = reporter.publish([
        issue,
    ], revision)
    assert len(issues) == 1
    assert len(patches) == 1

    # Check the callback has been used
    assert len(responses.calls) > 0
    call = responses.calls[-1]
    assert call.request.url == 'http://phabricator.test/api/differential.createcomment'
    assert call.response.headers.get('unittest') == 'clang-format'
def test_phabricator_coverage(mock_config, mock_repository, mock_phabricator):
    '''
    Test Phabricator reporter publication on a mock coverage issue
    '''
    from static_analysis_bot.report.phabricator import PhabricatorReporter
    from static_analysis_bot.revisions import PhabricatorRevision
    from static_analysis_bot.coverage import CoverageIssue

    def _check_comment(request):
        # Check the Phabricator main comment is well formed
        payload = urllib.parse.parse_qs(request.body)
        assert payload['output'] == ['json']
        assert len(payload['params']) == 1
        details = json.loads(payload['params'][0])
        assert details['message'] == VALID_COVERAGE_MESSAGE.format(
            results=mock_config.taskcluster.results_dir)

        # Outputs dummy empty response
        resp = {
            'error_code': None,
            'result': None,
        }
        return 201, {
            'Content-Type': 'application/json',
            'unittest': 'coverage'
        }, json.dumps(resp)

    responses.add_callback(
        responses.POST,
        'http://phabricator.test/api/differential.createcomment',
        callback=_check_comment,
    )

    with mock_phabricator as api:
        revision = PhabricatorRevision(api, 'PHID-DIFF-abcdef')
        revision.lines = {
            # Add dummy lines diff
            'test.txt': [0],
            'test.cpp': [0],
            'dom/test.cpp': [
                42,
            ],
        }
        reporter = PhabricatorReporter({'analyzers': ['coverage']}, api=api)

    issue = CoverageIssue('test.cpp', 0, 'This file is uncovered', revision)
    assert issue.is_publishable()

    issues, patches = reporter.publish([
        issue,
    ], revision)
    assert len(issues) == 1
    assert len(patches) == 0

    # Check the callback has been used
    assert len(responses.calls) > 0
    call = responses.calls[-1]
    assert call.request.url == 'http://phabricator.test/api/differential.createcomment'
    assert call.response.headers.get('unittest') == 'coverage'
Exemplo n.º 4
0
def test_phabricator_clang_tidy(mock_repository, mock_phabricator):
    '''
    Test Phabricator reporter publication on a mock clang-tidy issue
    '''
    from static_analysis_bot.report.phabricator import PhabricatorReporter
    from static_analysis_bot.revisions import PhabricatorRevision
    from static_analysis_bot.clang.tidy import ClangTidyIssue

    def _check_comment(request):
        # Check the Phabricator main comment is well formed
        payload = urllib.parse.parse_qs(request.body)
        assert payload['output'] == ['json']
        assert len(payload['params']) == 1
        details = json.loads(payload['params'][0])
        assert details == {
            'revision_id': 51,
            'message': VALID_CLANG_TIDY_MESSAGE,
            'attach_inlines': 1,
            '__conduit__': {'token': 'deadbeef'},
        }

        # Outputs dummy empty response
        resp = {
            'error_code': None,
            'result': None,
        }
        return 201, {'Content-Type': 'application/json', 'unittest': 'clang-tidy'}, json.dumps(resp)

    responses.add_callback(
        responses.POST,
        'http://phabricator.test/api/differential.createcomment',
        callback=_check_comment,
    )

    with mock_phabricator as api:
        revision = PhabricatorRevision('PHID-DIFF-abcdef', api)
        revision.lines = {
            # Add dummy lines diff
            'test.cpp': [41, 42, 43],
        }
        reporter = PhabricatorReporter({'analyzers': ['clang-tidy']}, api=api)

    issue_parts = ('test.cpp', '42', '51', 'error', 'dummy message', 'modernize-use-nullptr')
    issue = ClangTidyIssue(issue_parts, revision)
    assert issue.is_publishable()

    issues, patches = reporter.publish([issue, ], revision)
    assert len(issues) == 1
    assert len(patches) == 0

    # Check the callback has been used
    assert len(responses.calls) > 0
    call = responses.calls[-1]
    assert call.request.url == 'http://phabricator.test/api/differential.createcomment'
    assert call.response.headers.get('unittest') == 'clang-tidy'
def test_phabricator_clang_format(mock_config, mock_repository, mock_phabricator):
    '''
    Test Phabricator reporter publication on a mock clang-format issue
    '''
    from static_analysis_bot.report.phabricator import PhabricatorReporter
    from static_analysis_bot.revisions import PhabricatorRevision, ImprovementPatch
    from static_analysis_bot.clang.format import ClangFormatIssue

    def _check_comment(request):
        # Check the Phabricator main comment is well formed
        payload = urllib.parse.parse_qs(request.body)
        assert payload['output'] == ['json']
        assert len(payload['params']) == 1
        details = json.loads(payload['params'][0])
        assert details['message'] == VALID_CLANG_FORMAT_MESSAGE.format(results=mock_config.taskcluster.results_dir)

        # Outputs dummy empty response
        resp = {
            'error_code': None,
            'result': None,
        }
        return 201, {'Content-Type': 'application/json', 'unittest': 'clang-format'}, json.dumps(resp)

    responses.add_callback(
        responses.POST,
        'http://phabricator.test/api/differential.createcomment',
        callback=_check_comment,
    )

    with mock_phabricator as api:
        revision = PhabricatorRevision(api, 'PHID-DIFF-abcdef')
        revision.lines = {
            # Add dummy lines diff
            'test.cpp': [41, 42, 43],
            'dom/test.cpp': [42, ],
        }
        reporter = PhabricatorReporter({'analyzers': ['clang-format']}, api=api)

    issue = ClangFormatIssue('dom/test.cpp', 42, 1, revision)
    assert issue.is_publishable()

    revision.improvement_patches = [
        ImprovementPatch('clang-format', repr(revision), 'Some lint fixes'),
    ]
    list(map(lambda p: p.write(), revision.improvement_patches))  # trigger local write

    issues, patches = reporter.publish([issue, ], revision)
    assert len(issues) == 1
    assert len(patches) == 1

    # Check the callback has been used
    assert len(responses.calls) > 0
    call = responses.calls[-1]
    assert call.request.url == 'http://phabricator.test/api/differential.createcomment'
    assert call.response.headers.get('unittest') == 'clang-format'
def test_phabricator_clang_tidy(mock_repository, mock_phabricator):
    '''
    Test Phabricator reporter publication on a mock clang-tidy issue
    '''
    from static_analysis_bot.report.phabricator import PhabricatorReporter
    from static_analysis_bot.revisions import PhabricatorRevision
    from static_analysis_bot.clang.tidy import ClangTidyIssue

    def _check_comment(request):
        # Check the Phabricator main comment is well formed
        payload = urllib.parse.parse_qs(request.body)
        assert payload['output'] == ['json']
        assert len(payload['params']) == 1
        details = json.loads(payload['params'][0])
        assert details == {
            'revision_id': 51,
            'message': VALID_CLANG_TIDY_MESSAGE,
            'attach_inlines': 1,
            '__conduit__': {'token': 'deadbeef'},
        }

        # Outputs dummy empty response
        resp = {
            'error_code': None,
            'result': None,
        }
        return 201, {'Content-Type': 'application/json', 'unittest': 'clang-tidy'}, json.dumps(resp)

    responses.add_callback(
        responses.POST,
        'http://phabricator.test/api/differential.createcomment',
        callback=_check_comment,
    )

    with mock_phabricator as api:
        revision = PhabricatorRevision(api, 'PHID-DIFF-abcdef')
        revision.lines = {
            # Add dummy lines diff
            'test.cpp': [41, 42, 43],
        }
        reporter = PhabricatorReporter({'analyzers': ['clang-tidy'], 'modes': ('comment')}, api=api)

    issue = ClangTidyIssue(revision, 'test.cpp', '42', '51', 'modernize-use-nullptr', 'dummy message', 'error')
    assert issue.is_publishable()

    issues, patches = reporter.publish([issue, ], revision)
    assert len(issues) == 1
    assert len(patches) == 0

    # Check the callback has been used
    assert len(responses.calls) > 0
    call = responses.calls[-1]
    assert call.request.url == 'http://phabricator.test/api/differential.createcomment'
    assert call.response.headers.get('unittest') == 'clang-tidy'
def test_mail(mock_config, mock_issues, mock_phabricator):
    '''
    Test mail sending through Taskcluster
    '''
    from static_analysis_bot.report.mail import MailReporter
    from static_analysis_bot.revisions import PhabricatorRevision, ImprovementPatch

    def _check_email(request):
        payload = json.loads(request.body)

        assert payload['subject'] in (
            '[test] New Static Analysis Phabricator #42 - PHID-DIFF-test', )
        assert payload['address'] == '*****@*****.**'
        assert payload['template'] == 'fullscreen'
        assert payload['content'] == MAIL_CONTENT.format(
            results=mock_config.taskcluster.results_dir)

        return (200, {}, '')  # ack

    # Add mock taskcluster email to check output
    responses.add_callback(
        responses.POST,
        'https://notify.taskcluster.net/v1/email',
        callback=_check_email,
    )

    # Publish email
    conf = {
        'emails': [
            '*****@*****.**',
        ],
    }
    r = MailReporter(conf, 'test_tc', 'token_tc')

    with mock_phabricator as api:
        prev = PhabricatorRevision(api, 'PHID-DIFF-test')
        prev.improvement_patches = [
            ImprovementPatch('clang-tidy', repr(prev), 'Some code fixes'),
            ImprovementPatch('clang-format', repr(prev), 'Some lint fixes'),
        ]
        list(map(lambda p: p.write(),
                 prev.improvement_patches))  # trigger local write
        r.publish(mock_issues, prev)

    # Check stats
    mock_cls = mock_issues[0].__class__
    assert r.calc_stats(mock_issues) == {
        mock_cls: {
            'total': 5,
            'publishable': 3,
            'publishable_paths': ['/path/to/file']
        }
    }
def test_phabricator_coverage(mock_config, mock_repository, mock_phabricator):
    '''
    Test Phabricator reporter publication on a mock coverage issue
    '''
    from static_analysis_bot.report.phabricator import PhabricatorReporter
    from static_analysis_bot.revisions import PhabricatorRevision
    from static_analysis_bot.coverage import CoverageIssue

    def _check_comment(request):
        # Check the Phabricator main comment is well formed
        payload = urllib.parse.parse_qs(request.body)
        assert payload['output'] == ['json']
        assert len(payload['params']) == 1
        details = json.loads(payload['params'][0])
        assert details['message'] == VALID_COVERAGE_MESSAGE.format(results=mock_config.taskcluster.results_dir)

        # Outputs dummy empty response
        resp = {
            'error_code': None,
            'result': None,
        }
        return 201, {'Content-Type': 'application/json', 'unittest': 'coverage'}, json.dumps(resp)

    responses.add_callback(
        responses.POST,
        'http://phabricator.test/api/differential.createcomment',
        callback=_check_comment,
    )

    with mock_phabricator as api:
        revision = PhabricatorRevision(api, 'PHID-DIFF-abcdef')
        revision.lines = {
            # Add dummy lines diff
            'test.txt': [0],
            'test.cpp': [0],
            'dom/test.cpp': [42, ],
        }
        reporter = PhabricatorReporter({'analyzers': ['coverage']}, api=api)

    issue = CoverageIssue('test.cpp', 0, 'This file is uncovered', revision)
    assert issue.is_publishable()

    issues, patches = reporter.publish([issue, ], revision)
    assert len(issues) == 1
    assert len(patches) == 0

    # Check the callback has been used
    assert len(responses.calls) > 0
    call = responses.calls[-1]
    assert call.request.url == 'http://phabricator.test/api/differential.createcomment'
    assert call.response.headers.get('unittest') == 'coverage'
def test_mail(mock_issues, mock_phabricator):
    '''
    Test mail sending through Taskcluster
    '''
    from static_analysis_bot.report.mail import MailReporter
    from static_analysis_bot.revisions import PhabricatorRevision

    def _check_email(request):
        payload = json.loads(request.body)

        assert payload['subject'] in (
            '[test] New Static Analysis Phabricator #42 - PHID-DIFF-test', )
        assert payload['address'] == '*****@*****.**'
        assert payload['template'] == 'fullscreen'
        assert payload['content'] == MAIL_CONTENT

        return (200, {}, '')  # ack

    # Add mock taskcluster email to check output
    responses.add_callback(
        responses.POST,
        'https://notify.taskcluster.net/v1/email',
        callback=_check_email,
    )

    # Publish email
    conf = {
        'emails': [
            '*****@*****.**',
        ],
    }
    r = MailReporter(conf, 'test_tc', 'token_tc')

    with mock_phabricator as api:
        prev = PhabricatorRevision('PHID-DIFF-test', api)
        prev.improvement_patches = {
            'clang-tidy': 'https://my.patch/clang-tidy.diff',
            'clang-format': 'https://another.patch/clang-format.diff',
        }
        r.publish(mock_issues, prev)

    # Check stats
    mock_cls = mock_issues[0].__class__
    assert r.calc_stats(mock_issues) == {
        mock_cls: {
            'total': 5,
            'publishable': 3,
        }
    }
Exemplo n.º 10
0
def mock_revision(mock_phabricator):
    '''
    Mock a mercurial revision
    '''
    from static_analysis_bot.revisions import PhabricatorRevision
    with mock_phabricator as api:
        return PhabricatorRevision(api, diff_phid='PHID-DIFF-XXX')
Exemplo n.º 11
0
def test_phabricator(mock_phabricator, mock_config):
    '''
    Test a phabricator revision
    '''
    from static_analysis_bot.revisions import PhabricatorRevision

    with mock_phabricator as api:
        r = PhabricatorRevision(api, 'PHID-DIFF-testABcd12')
    assert not hasattr(r, 'mercurial')
    assert r.diff_id == 42
    assert r.diff_phid == 'PHID-DIFF-testABcd12'
    assert r.url == 'https://phabricator.test/D51'
    assert repr(r) == 'PHID-DIFF-testABcd12'
    assert r.id == 51  # revision

    # Patch is automatically loaded from Phabricator
    assert r.patch is not None
    assert isinstance(r.patch, str)
    assert len(r.patch.split('\n')) == 14
    patch = Patch.parse_patch(r.patch)
    assert patch == {
        'test.txt': {
            'touched': [],
            'deleted': [],
            'added': [2],
            'new': False
        },
        'test.cpp': {
            'touched': [],
            'deleted': [],
            'added': [2],
            'new': False
        }
    }
    def _test_reporter(api, analyzers):
        # Always use the same setup, only varies the analyzers
        revision = PhabricatorRevision(api, 'PHID-DIFF-abcdef')
        revision.lines = {
            'test.cpp': [0, 41, 42, 43],
            'dom/test.cpp': [
                42,
            ],
        }
        reporter = PhabricatorReporter({'analyzers': analyzers}, api=api)

        issues = [
            ClangFormatIssue('dom/test.cpp', 42, 1, revision),
            ClangTidyIssue(('test.cpp', '42', '51', 'error', 'dummy message',
                            'modernize-use-nullptr'), revision),
            InferIssue(
                {
                    'file': 'test.cpp',
                    'line': 42,
                    'column': 1,
                    'bug_type': 'dummy',
                    'kind': 'whatever',
                    'qualifier': 'dummy message.',
                }, revision),
            MozLintIssue('test.cpp', 1, 'danger', 42, 'flake8', 'Python error',
                         'EXXX', revision),
            CoverageIssue('test.cpp', 0, 'This file is uncovered', revision),
        ]

        assert all(i.is_publishable() for i in issues)

        revision.improvement_patches = [
            ImprovementPatch('dummy', repr(revision), 'Whatever'),
            ImprovementPatch('clang-tidy', repr(revision), 'Some C fixes'),
            ImprovementPatch('clang-format', repr(revision),
                             'Some lint fixes'),
            ImprovementPatch('infer', repr(revision), 'Some java fixes'),
            ImprovementPatch('mozlint', repr(revision), 'Some js fixes'),
        ]
        list(map(lambda p: p.write(),
                 revision.improvement_patches))  # trigger local write

        return reporter.publish(issues, revision)
Exemplo n.º 13
0
def test_mail(mock_issues, mock_phabricator):
    '''
    Test mail sending through Taskcluster
    '''
    from static_analysis_bot.report.mail import MailReporter
    from static_analysis_bot.revisions import MozReviewRevision, PhabricatorRevision

    def _check_email(request):
        payload = json.loads(request.body)

        assert payload['subject'] in (
            '[test] New Static Analysis MozReview #12345 - 1',
            '[test] New Static Analysis Phabricator #42 - PHID-DIFF-test',
        )
        assert payload['address'] == '*****@*****.**'
        assert payload['template'] == 'fullscreen'
        assert payload['content'].startswith(
            '\n# Found 3 publishable issues (5 total)')

        return (200, {}, '')  # ack

    # Add mock taskcluster email to check output
    responses.add_callback(
        responses.POST,
        'https://notify.taskcluster.net/v1/email',
        callback=_check_email,
    )

    # Publish email
    conf = {
        'emails': [
            '*****@*****.**',
        ],
    }
    r = MailReporter(conf, 'test_tc', 'token_tc')

    # Publish for mozreview
    mrev = MozReviewRevision('12345', 'abcdef', '1')
    r.publish(mock_issues, mrev)

    with mock_phabricator as api:
        prev = PhabricatorRevision('PHID-DIFF-test', api)
        r.publish(mock_issues, prev)

    # Check stats
    mock_cls = mock_issues[0].__class__
    assert r.calc_stats(mock_issues) == {
        mock_cls: {
            'total': 5,
            'publishable': 3,
        }
    }
    def _test_reporter(api, analyzers):
        # Always use the same setup, only varies the analyzers
        revision = PhabricatorRevision(api, 'PHID-DIFF-abcdef')
        revision.lines = {
            'test.cpp': [0, 41, 42, 43],
            'dom/test.cpp': [42, ],
        }
        reporter = PhabricatorReporter({'analyzers': analyzers}, api=api)

        issues = [
            ClangFormatIssue('dom/test.cpp', 42, 1, revision),
            ClangTidyIssue(revision, 'test.cpp', '42', '51', 'modernize-use-nullptr', 'dummy message', 'error'),
            InferIssue({
                'file': 'test.cpp',
                'line': 42,
                'column': 1,
                'bug_type': 'dummy',
                'kind': 'whatever',
                'qualifier': 'dummy message.',
            }, revision),
            MozLintIssue('test.cpp', 1, 'danger', 42, 'flake8', 'Python error', 'EXXX', revision),
            CoverageIssue('test.cpp', 0, 'This file is uncovered', revision),
        ]

        assert all(i.is_publishable() for i in issues)

        revision.improvement_patches = [
            ImprovementPatch('dummy', repr(revision), 'Whatever'),
            ImprovementPatch('clang-tidy', repr(revision), 'Some C fixes'),
            ImprovementPatch('clang-format', repr(revision), 'Some lint fixes'),
            ImprovementPatch('infer', repr(revision), 'Some java fixes'),
            ImprovementPatch('mozlint', repr(revision), 'Some js fixes'),
        ]
        list(map(lambda p: p.write(), revision.improvement_patches))  # trigger local write

        return reporter.publish(issues, revision)
def test_publication(tmpdir, mock_issues, mock_phabricator):
    '''
    Test debug publication and report analysis
    '''
    from static_analysis_bot.report.debug import DebugReporter
    from static_analysis_bot.revisions import PhabricatorRevision

    report_dir = str(tmpdir.mkdir('public').realpath())
    report_path = os.path.join(report_dir, 'report.json')
    assert not os.path.exists(report_path)

    with mock_phabricator as api:
        prev = PhabricatorRevision(api, 'PHID-DIFF-abcdef')

    r = DebugReporter(report_dir)
    r.publish(mock_issues, prev)

    assert os.path.exists(report_path)
    with open(report_path) as f:
        report = json.load(f)

    assert 'issues' in report
    assert report['issues'] == [{
        'nb': 0
    }, {
        'nb': 1
    }, {
        'nb': 2
    }, {
        'nb': 3
    }, {
        'nb': 4
    }]

    assert 'revision' in report
    assert report['revision'] == {
        'id': 51,
        'diff_id': 42,
        'url': 'https://phabricator.test/D51',
        'bugzilla_id': '',
        'diff_phid': 'PHID-DIFF-abcdef',
        'phid': 'PHID-DREV-zzzzz',
        'title': 'Static Analysis tests',
        'has_clang_files': False,
    }

    assert 'time' in report
    assert isinstance(report['time'], float)
def test_phabricator(mock_phabricator, mock_repository, mock_config):
    '''
    Test a phabricator revision
    '''
    from static_analysis_bot.revisions import PhabricatorRevision

    with mock_phabricator as api:
        r = PhabricatorRevision(api, 'PHID-DIFF-testABcd12')
    assert not hasattr(r, 'mercurial')
    assert r.diff_id == 42
    assert r.diff_phid == 'PHID-DIFF-testABcd12'
    assert r.url == 'https://phabricator.test/D51'
    assert repr(r) == 'PHID-DIFF-testABcd12'
    assert r.id == 51  # revision

    # Check test.txt content
    test_txt = os.path.join(mock_config.repo_dir, 'test.txt')
    assert open(test_txt).read() == 'Hello World\n'

    # Cleanup the repo
    mock_repository.update(clean=True)

    # Load full patch
    # Mock the mercurial repo update as we use a dummy revision
    assert r.patch is None
    __update = mock_repository.update
    mock_repository.update = MagicMock(return_value=True)
    r.load(mock_repository)
    mock_repository.update = __update
    assert r.patch is not None
    assert isinstance(r.patch, str)
    assert len(r.patch.split('\n')) == 14
    patch = Patch.parse_patch(r.patch)
    assert patch == {
        'test.txt': {
            'touched': [],
            'deleted': [],
            'added': [2],
            'new': False
        },
        'test.cpp': {
            'touched': [],
            'deleted': [],
            'added': [2],
            'new': False
        }
    }

    # Check file is untouched after load
    assert open(test_txt).read() == 'Hello World\n'

    # Check file is updated after apply
    r.apply(mock_repository)
    assert open(test_txt).read() == 'Hello World\nSecond line\n'
Exemplo n.º 17
0
def main(
    id,
    work_dir,
    taskcluster_secret,
    taskcluster_client_id,
    taskcluster_access_token,
):

    secrets = get_secrets(
        taskcluster_secret,
        config.PROJECT_NAME,
        required=(
            'APP_CHANNEL',
            'REPORTERS',
            'ANALYZERS',
            'PHABRICATOR',
            'ALLOWED_PATHS',
            'MAX_CLONE_RUNTIME',
        ),
        existing={
            'APP_CHANNEL': 'development',
            'REPORTERS': [],
            'ANALYZERS': [
                'clang-tidy',
            ],
            'PUBLICATION': 'IN_PATCH',
            'ALLOWED_PATHS': [
                '*',
            ],
            'MAX_CLONE_RUNTIME': 15 * 60,
        },
        taskcluster_client_id=taskcluster_client_id,
        taskcluster_access_token=taskcluster_access_token,
    )

    init_logger(
        config.PROJECT_NAME,
        PAPERTRAIL_HOST=secrets.get('PAPERTRAIL_HOST'),
        PAPERTRAIL_PORT=secrets.get('PAPERTRAIL_PORT'),
        SENTRY_DSN=secrets.get('SENTRY_DSN'),
        MOZDEF=secrets.get('MOZDEF'),
        timestamp=True,
    )

    # Setup settings before stats
    settings.setup(
        secrets['APP_CHANNEL'],
        work_dir,
        secrets['PUBLICATION'],
        secrets['ALLOWED_PATHS'],
        secrets.get('COVERITY_CONFIG'),
        secrets['MAX_CLONE_RUNTIME'],
    )
    # Setup statistics
    datadog_api_key = secrets.get('DATADOG_API_KEY')
    if datadog_api_key:
        stats.auth(datadog_api_key)

    # Load reporters
    reporters = get_reporters(
        secrets['REPORTERS'],
        taskcluster_client_id,
        taskcluster_access_token,
    )

    # Load index service
    index_service = get_service(
        'index',
        taskcluster_client_id,
        taskcluster_access_token,
    )

    # Load queue service
    queue_service = get_service(
        'queue',
        taskcluster_client_id,
        taskcluster_access_token,
    )

    # Load Phabricator API
    phabricator_api = PhabricatorAPI(**secrets['PHABRICATOR'])
    if 'phabricator' in reporters:
        reporters['phabricator'].setup_api(phabricator_api)

    # Load unique revision
    if settings.source == SOURCE_PHABRICATOR:
        revision = PhabricatorRevision(phabricator_api, diff_phid=id)
    elif settings.source == SOURCE_TRY:
        revision = PhabricatorRevision(phabricator_api,
                                       try_task=queue_service.task(
                                           settings.try_task_id))
    else:
        raise Exception('Unsupported source {}'.format(settings.source))

    # Run workflow according to source
    w = Workflow(reporters, secrets['ANALYZERS'], index_service, queue_service,
                 phabricator_api)
    try:
        w.run(revision)
    except Exception as e:
        # Log errors to papertrail
        logger.error(
            'Static analysis failure',
            revision=revision,
            error=e,
        )

        # Index analysis state
        extras = {}
        if isinstance(e, AnalysisException):
            extras['error_code'] = e.code
            extras['error_message'] = str(e)
        w.index(revision, state='error', **extras)

        # Then raise to mark task as erroneous
        raise
def test_phabricator_harbormaster(mock_repository, mock_phabricator):
    '''
    Test Phabricator reporter publication on a mock clang-tidy issue
    using harbormaster
    '''
    from static_analysis_bot.report.phabricator import PhabricatorReporter
    from static_analysis_bot.revisions import PhabricatorRevision
    from static_analysis_bot.clang.tidy import ClangTidyIssue

    def _check_message(request):
        # Check the Phabricator main comment is well formed
        payload = urllib.parse.parse_qs(request.body)
        assert payload['output'] == ['json']
        assert len(payload['params']) == 1
        details = json.loads(payload['params'][0])
        assert details == {
            'buildTargetPHID': 'PHID-HMBD-deadbeef12456',
            'lint': [
                {
                    'char': 51,
                    'code': 'clang-tidy.modernize-use-nullptr',
                    'name': 'Clang-Tidy - modernize-use-nullptr',
                    'line': 42,
                    'path': 'test.cpp',
                    'severity': 'warning',
                    'description': 'dummy message'
                }
            ],
            'unit': [],
            'type': 'work',
            '__conduit__': {'token': 'deadbeef'},
        }

        # Outputs dummy empty response
        resp = {
            'error_code': None,
            'result': None,
        }
        return 201, {'Content-Type': 'application/json', 'unittest': 'clang-tidy'}, json.dumps(resp)

    responses.add_callback(
        responses.POST,
        'http://phabricator.test/api/harbormaster.sendmessage',
        callback=_check_message,
    )

    with mock_phabricator as api:
        revision = PhabricatorRevision(api, 'PHID-DIFF-abcdef')
        revision.lines = {
            # Add dummy lines diff
            'test.cpp': [41, 42, 43],
        }
        revision.build_target_phid = 'PHID-HMBD-deadbeef12456'
        reporter = PhabricatorReporter({'analyzers': ['clang-tidy'], 'mode': 'harbormaster'}, api=api)

    issue = ClangTidyIssue(revision, 'test.cpp', '42', '51', 'modernize-use-nullptr', 'dummy message', 'error')
    assert issue.is_publishable()

    issues, patches = reporter.publish([issue, ], revision)
    assert len(issues) == 1
    assert len(patches) == 0

    # Check the callback has been used
    assert len(responses.calls) > 0
    call = responses.calls[-1]
    assert call.request.url == 'http://phabricator.test/api/harbormaster.sendmessage'
    assert call.response.headers.get('unittest') == 'clang-tidy'
Exemplo n.º 19
0
def main(
    taskcluster_secret,
    taskcluster_client_id,
    taskcluster_access_token,
):

    secrets = get_secrets(
        taskcluster_secret,
        config.PROJECT_NAME,
        required=(
            'APP_CHANNEL',
            'REPORTERS',
            'PHABRICATOR',
            'ALLOWED_PATHS',
        ),
        existing={
            'APP_CHANNEL': 'development',
            'REPORTERS': [],
            'PUBLICATION': 'IN_PATCH',
            'ALLOWED_PATHS': [
                '*',
            ],
        },
        taskcluster_client_id=taskcluster_client_id,
        taskcluster_access_token=taskcluster_access_token,
    )

    init_logger(
        config.PROJECT_NAME,
        PAPERTRAIL_HOST=secrets.get('PAPERTRAIL_HOST'),
        PAPERTRAIL_PORT=secrets.get('PAPERTRAIL_PORT'),
        SENTRY_DSN=secrets.get('SENTRY_DSN'),
        MOZDEF=secrets.get('MOZDEF'),
        timestamp=True,
    )

    # Setup settings before stats
    phabricator = secrets['PHABRICATOR']
    settings.setup(
        secrets['APP_CHANNEL'],
        secrets['PUBLICATION'],
        secrets['ALLOWED_PATHS'],
        secrets.get('COVERITY_CONFIG'),
    )
    # Setup statistics
    datadog_api_key = secrets.get('DATADOG_API_KEY')
    if datadog_api_key:
        stats.auth(datadog_api_key)

    # Load reporters
    reporters = get_reporters(
        secrets['REPORTERS'],
        taskcluster_client_id,
        taskcluster_access_token,
    )

    # Load index service
    index_service = get_service(
        'index',
        taskcluster_client_id,
        taskcluster_access_token,
    )

    # Load queue service
    queue_service = get_service(
        'queue',
        taskcluster_client_id,
        taskcluster_access_token,
    )

    # Load Phabricator API
    phabricator_reporting_enabled = 'phabricator' in reporters
    phabricator_api = PhabricatorAPI(phabricator['api_key'],
                                     phabricator['url'])
    if phabricator_reporting_enabled:
        reporters['phabricator'].setup_api(phabricator_api)

    # Load unique revision
    revision = PhabricatorRevision(
        phabricator_api,
        try_task=queue_service.task(settings.try_task_id),

        # Update build status only when phabricator reporting is enabled
        update_build=phabricator_reporting_enabled,
    )

    # Run workflow according to source
    w = Workflow(reporters, index_service, queue_service, phabricator_api)
    try:
        w.run(revision)
    except Exception as e:
        # Log errors to papertrail
        logger.error(
            'Static analysis failure',
            revision=revision,
            error=e,
        )

        # Index analysis state
        extras = {}
        if isinstance(e, AnalysisException):
            extras['error_code'] = e.code
            extras['error_message'] = str(e)
        w.index(revision, state='error', **extras)

        # Update Harbormaster status
        revision.update_status(state=BuildState.Fail)

        # Then raise to mark task as erroneous
        raise
Exemplo n.º 20
0
def main(source,
         id,
         cache_root,
         mozreview_diffset,
         mozreview_revision,
         taskcluster_secret,
         taskcluster_client_id,
         taskcluster_access_token,
         ):

    secrets = get_secrets(taskcluster_secret,
                          config.PROJECT_NAME,
                          required=(
                              'APP_CHANNEL',
                              'REPORTERS',
                              'ANALYZERS',
                              'PHABRICATOR',
                          ),
                          existing={
                              'APP_CHANNEL': 'development',
                              'REPORTERS': [],
                              'ANALYZERS': ['clang-tidy', ],
                              'PUBLICATION': 'IN_PATCH',
                          },
                          taskcluster_client_id=taskcluster_client_id,
                          taskcluster_access_token=taskcluster_access_token,
                          )

    init_logger(config.PROJECT_NAME,
                PAPERTRAIL_HOST=secrets.get('PAPERTRAIL_HOST'),
                PAPERTRAIL_PORT=secrets.get('PAPERTRAIL_PORT'),
                SENTRY_DSN=secrets.get('SENTRY_DSN'),
                MOZDEF=secrets.get('MOZDEF'),
                )

    # Setup settings before stats
    settings.setup(secrets['APP_CHANNEL'], cache_root, secrets['PUBLICATION'])

    # Setup statistics
    datadog_api_key = secrets.get('DATADOG_API_KEY')
    if datadog_api_key:
        stats.auth(datadog_api_key)

    # Load reporters
    reporters = get_reporters(
        secrets['REPORTERS'],
        taskcluster_client_id,
        taskcluster_access_token,
    )

    # Load index service
    index_service = get_service(
        'index',
        taskcluster_client_id,
        taskcluster_access_token,
    )

    # Load Phabricator API
    phabricator_api = PhabricatorAPI(**secrets['PHABRICATOR'])
    if 'phabricator' in reporters:
        reporters['phabricator'].setup_api(phabricator_api)

    # Load unique revision
    if source == 'phabricator':
        revision = PhabricatorRevision(id, phabricator_api)

    elif source == 'mozreview':
        revision = MozReviewRevision(id, mozreview_revision, mozreview_diffset)

    else:
        raise Exception('Unsupported analysis source: {}'.format(source))

    w = Workflow(reporters, secrets['ANALYZERS'], index_service, phabricator_api)
    try:
        w.run(revision)
    except Exception as e:
        # Log errors to papertrail
        logger.error(
            'Static analysis failure',
            revision=revision,
            error=e,
        )

        # Index analysis state
        w.index(revision, state='error')

        # Then raise to mark task as erroneous
        raise
def test_phabricator_clang_tidy_and_coverage(mock_config, mock_phabricator):
    '''
    Test Phabricator reporter publication on a mock coverage issue
    '''
    from static_analysis_bot.report.phabricator import PhabricatorReporter
    from static_analysis_bot.revisions import PhabricatorRevision
    from static_analysis_bot.tasks.coverage import CoverageIssue
    from static_analysis_bot.tasks.clang_tidy import ClangTidyIssue

    def _check_comment_sa(request):
        # Check the Phabricator main comment is well formed
        payload = urllib.parse.parse_qs(request.body)
        assert payload['output'] == ['json']
        assert len(payload['params']) == 1
        details = json.loads(payload['params'][0])
        assert details == {
            'revision_id': 51,
            'message': VALID_CLANG_TIDY_MESSAGE,
            'attach_inlines': 1,
            '__conduit__': {'token': 'deadbeef'},
        }

        # Outputs dummy empty response
        resp = {
            'error_code': None,
            'result': None,
        }
        return 201, {'Content-Type': 'application/json', 'unittest': 'clang-tidy'}, json.dumps(resp)

    def _check_comment_ccov(request):
        # Check the Phabricator main comment is well formed
        payload = urllib.parse.parse_qs(request.body)
        assert payload['output'] == ['json']
        assert len(payload['params']) == 1
        details = json.loads(payload['params'][0])
        assert details['message'] == VALID_COVERAGE_MESSAGE.format(results=mock_config.taskcluster.results_dir)

        # Outputs dummy empty response
        resp = {
            'error_code': None,
            'result': None,
        }
        return 201, {'Content-Type': 'application/json', 'unittest': 'coverage'}, json.dumps(resp)

    responses.add_callback(
        responses.POST,
        'http://phabricator.test/api/differential.createcomment',
        callback=_check_comment_sa,
    )

    responses.add_callback(
        responses.POST,
        'http://phabricator.test/api/differential.createcomment',
        callback=_check_comment_ccov,
    )

    with mock_phabricator as api:
        revision = PhabricatorRevision(api, 'PHID-DIFF-abcdef')
        revision.lines = {
            # Add dummy lines diff
            'test.txt': [0],
            'test.cpp': [0],
            'another_test.cpp': [41, 42, 43],
        }
        revision.files = ['test.txt', 'test.cpp', 'another_test.cpp']
        reporter = PhabricatorReporter({'analyzers': ['coverage', 'clang-tidy']}, api=api)

    issue_clang_tidy = ClangTidyIssue(revision, 'another_test.cpp', '42', '51', 'modernize-use-nullptr', 'dummy message', 'error')
    assert issue_clang_tidy.is_publishable()

    issue_coverage = CoverageIssue('test.cpp', 0, 'This file is uncovered', revision)
    assert issue_coverage.is_publishable()

    issues, patches = reporter.publish([issue_clang_tidy, issue_coverage, ], revision)
    assert len(issues) == 2
    assert len(patches) == 0

    # Check the callback has been used
    assert len(responses.calls) > 0
    call = responses.calls[-2]
    assert call.request.url == 'http://phabricator.test/api/differential.createcomment'
    assert call.response.headers.get('unittest') == 'clang-tidy'

    # Check the callback has been used
    assert len(responses.calls) > 0
    call = responses.calls[-1]
    assert call.request.url == 'http://phabricator.test/api/differential.createcomment'
    assert call.response.headers.get('unittest') == 'coverage'
def test_phabricator_harbormaster(mock_phabricator):
    '''
    Test Phabricator reporter publication on a mock clang-tidy issue
    using harbormaster
    '''
    from static_analysis_bot.report.phabricator import PhabricatorReporter
    from static_analysis_bot.revisions import PhabricatorRevision
    from static_analysis_bot.tasks.clang_tidy import ClangTidyIssue

    def _check_message(request):
        # Check the Phabricator main comment is well formed
        payload = urllib.parse.parse_qs(request.body)
        assert payload['output'] == ['json']
        assert len(payload['params']) == 1
        details = json.loads(payload['params'][0])
        assert details == {
            'buildTargetPHID': 'PHID-HMBD-deadbeef12456',
            'lint': [
                {
                    'char': 51,
                    'code': 'clang-tidy.modernize-use-nullptr',
                    'name': 'Clang-Tidy - modernize-use-nullptr',
                    'line': 42,
                    'path': 'test.cpp',
                    'severity': 'warning',
                    'description': 'dummy message'
                }
            ],
            'unit': [],
            'type': 'work',
            '__conduit__': {'token': 'deadbeef'},
        }

        # Outputs dummy empty response
        resp = {
            'error_code': None,
            'result': None,
        }
        return 201, {'Content-Type': 'application/json', 'unittest': 'clang-tidy'}, json.dumps(resp)

    responses.add_callback(
        responses.POST,
        'http://phabricator.test/api/harbormaster.sendmessage',
        callback=_check_message,
    )

    with mock_phabricator as api:
        revision = PhabricatorRevision(api, 'PHID-DIFF-abcdef')
        revision.lines = {
            # Add dummy lines diff
            'test.cpp': [41, 42, 43],
        }
        revision.build_target_phid = 'PHID-HMBD-deadbeef12456'
        reporter = PhabricatorReporter({'analyzers': ['clang-tidy'], 'mode': 'harbormaster'}, api=api)

    issue = ClangTidyIssue(revision, 'test.cpp', '42', '51', 'modernize-use-nullptr', 'dummy message', 'error')
    assert issue.is_publishable()

    issues, patches = reporter.publish([issue, ], revision)
    assert len(issues) == 1
    assert len(patches) == 0

    # Check the callback has been used
    assert len(responses.calls) > 0
    call = responses.calls[-1]
    assert call.request.url == 'http://phabricator.test/api/harbormaster.sendmessage'
    assert call.response.headers.get('unittest') == 'clang-tidy'