def test_as_text(mock_revision):
    '''
    Test text export for ClangTidyIssue
    '''
    from static_analysis_bot.clang.tidy import ClangTidyIssue
    issue = ClangTidyIssue(mock_revision, 'test.cpp', '42', '51', 'dummy-check', 'dummy message withUppercaseChars', 'error')
    issue.body = 'Dummy body withUppercaseChars'

    assert issue.as_text() == 'Error: Dummy message withUppercaseChars [clang-tidy: dummy-check]\n```\nDummy body withUppercaseChars\n```'
Пример #2
0
def test_as_markdown(mock_revision):
    '''
    Test markdown generation for ClangTidyIssue
    '''
    from static_analysis_bot.clang.tidy import ClangTidyIssue
    parts = ('test.cpp', '42', '51', 'error', 'dummy message', 'dummy-check')
    issue = ClangTidyIssue(parts, mock_revision)
    issue.body = 'Dummy body'

    assert issue.as_markdown() == '''
Пример #3
0
def test_expanded_macros(mock_stats, test_cpp, mock_revision):
    '''
    Test expanded macros are detected by clang issue
    '''
    from static_analysis_bot.clang.tidy import ClangTidyIssue
    parts = ('test.cpp', '42', '51', 'error', 'dummy message', 'dummy-check')
    issue = ClangTidyIssue(parts, mock_revision)
    assert issue.is_problem()
    assert issue.line == 42
    assert issue.char == 51
    assert issue.notes == []
    assert issue.is_expanded_macro() is False

    # Add a note starting with "expanded from macro..."
    parts = ('test.cpp', '42', '51', 'note', 'expanded from macro Blah dummy.cpp', 'dummy-check-note')
    issue.notes.append(ClangTidyIssue(parts, mock_revision))
    assert issue.is_expanded_macro() is True

    # Add another note does not change it
    parts = ('test.cpp', '42', '51', 'note', 'This is not an expanded macro', 'dummy-check-note')
    issue.notes.append(ClangTidyIssue(parts, mock_revision))
    assert issue.is_expanded_macro() is True

    # But if we swap them, it does not work anymore
    issue.notes.reverse()
    assert issue.is_expanded_macro() is False
Пример #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_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'
Пример #6
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)
def test_as_markdown(mock_revision):
    '''
    Test markdown generation for ClangTidyIssue
    '''
    from static_analysis_bot.clang.tidy import ClangTidyIssue
    issue = ClangTidyIssue(mock_revision, 'test.cpp', '42', '51', 'dummy-check', 'dummy message', 'error')
    issue.body = 'Dummy body'

    assert issue.as_markdown() == '''
## clang-tidy error

- **Message**: dummy message
- **Location**: test.cpp:42:51
- **In patch**: no
- **Clang check**: dummy-check
- **Publishable check**: no
- **Third Party**: no
- **Expanded Macro**: no
- **Publishable **: no
- **Is new**: no

```
Dummy body
```


'''
    assert issue.as_phabricator_lint() == {
        'char': 51,
        'code': 'clang-tidy.dummy-check',
        'line': 42,
        'name': 'Clang-Tidy - dummy-check',
        'description': 'dummy message\n\n > Dummy body',
        'path': 'test.cpp',
        'severity': 'warning',
    }
    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)
Пример #9
0
def test_expanded_macros(mock_stats, test_cpp, mock_revision):
    '''
    Test expanded macros are detected by clang issue
    '''
    from static_analysis_bot.clang.tidy import ClangTidyIssue
    issue = ClangTidyIssue(mock_revision, 'test.cpp', '42', '51', 'dummy message', 'dummy-check', 'error')
    assert issue.is_problem()
    assert issue.line == 42
    assert issue.char == 51
    assert issue.notes == []
    assert issue.is_expanded_macro() is False

    # Add a note starting with "expanded from macro..."
    issue.notes.append(ClangTidyIssue(mock_revision, 'test.cpp', '42', '51', 'dummy-check-note', 'expanded from macro Blah dummy.cpp', 'note'))
    assert issue.is_expanded_macro() is True

    # Add another note does not change it
    issue.notes.append(ClangTidyIssue(mock_revision, 'test.cpp', '42', '51', 'dummy-check-note', 'This is not an expanded macro', 'note'))
    assert issue.is_expanded_macro() is True

    # But if we swap them, it does not work anymore
    issue.notes.reverse()
    assert issue.is_expanded_macro() is False
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'
Пример #11
0
def test_comment(mock_mozreview, test_cpp, mock_revision):
    '''
    Test comment creation for specific issues
    '''
    from static_analysis_bot.clang.tidy import ClangTidyIssue
    from static_analysis_bot.clang.format import ClangFormatIssue
    from static_analysis_bot.lint import MozLintIssue
    from static_analysis_bot.report.base import Reporter

    # Init dummy reporter
    class TestReporter(Reporter):
        def __init__(self):
            pass

    reporter = TestReporter()

    # Build clang tidy fake issue, while forcing publication status
    header = ('test.cpp', 1, 1, 'error', 'Dummy message', 'test-check')
    clang_tidy_publishable = ClangTidyIssue(header, mock_revision)
    clang_tidy_publishable.is_publishable = lambda: True
    assert clang_tidy_publishable.is_publishable()
    issues = [
        clang_tidy_publishable,
    ]

    assert reporter.build_comment(issues, 'https://report.example.com') == '''
Code analysis found 1 defect in this patch:
 - 1 defect found by clang-tidy

You can run this analysis locally with:
 - `./mach static-analysis check path/to/file.cpp` (C/C++)


If you see a problem in this automated review, please report it here: https://report.example.com
'''

    # Now add a clang-format issue
    clang_format_publishable = ClangFormatIssue('test.cpp', '', '',
                                                ('delete', 1, 2, 3, 4),
                                                mock_revision)
    clang_format_publishable.is_publishable = lambda: True
    assert clang_tidy_publishable.is_publishable()
    issues.append(clang_format_publishable)

    assert reporter.build_comment(issues, 'https://report.example.com') == '''
Code analysis found 2 defects in this patch:
 - 1 defect found by clang-format
 - 1 defect found by clang-tidy

You can run this analysis locally with:
 - `./mach clang-format -p path/to/file.cpp` (C/C++)
 - `./mach static-analysis check path/to/file.cpp` (C/C++)


If you see a problem in this automated review, please report it here: https://report.example.com
'''

    # Now add a mozlint issue
    mozlint_publishable = MozLintIssue('test.cpp', 1, 'error', 1, 'test',
                                       'Dummy test', 'dummy rule',
                                       mock_revision)
    mozlint_publishable.is_publishable = lambda: True
    assert mozlint_publishable.is_publishable()
    issues.append(mozlint_publishable)

    assert reporter.build_comment(issues, 'https://report.example.com') == '''
Пример #12
0
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'