def test_phabricator_coverage(mock_config, mock_phabricator, mock_try_task): """ Test Phabricator reporter publication on a mock coverage issue """ from code_review_bot.report.phabricator import PhabricatorReporter from code_review_bot.revisions import Revision from code_review_bot.tasks.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 # 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 = Revision(api, mock_try_task) revision.lines = { # Add dummy lines diff "test.txt": [0], "path/to/test.cpp": [0], "dom/test.cpp": [42], } reporter = PhabricatorReporter({"analyzers": ["coverage"]}, api=api) issue = CoverageIssue("path/to/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_reporter(api, analyzers): # Always use the same setup, only varies the analyzers revision = Revision(api, mock_try_task) 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_full_file(mock_config, mock_phabricator, mock_try_task): """ Test Phabricator reporter supports an issue on a full file """ from code_review_bot.report.phabricator import PhabricatorReporter from code_review_bot.revisions import Revision from code_review_bot.tasks.default import DefaultIssue 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_DEFAULT_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": "full-file-comment" }, json.dumps(resp), ) def _check_inline(request): # Check the inline does not have a null value for line payload = urllib.parse.parse_qs(request.body) assert payload["output"] == ["json"] assert len(payload["params"]) == 1 details = json.loads(payload["params"][0]) assert details == { "__conduit__": { "token": "deadbeef" }, "content": "Error: Something bad happened on the whole file ! [a-huge-issue]", "diffID": 42, "filePath": "xx.cpp", "isNewFile": 1, "lineLength": -1, # Cannot be null for a full file as it's not supported by phabricator "lineNumber": 1, } # Outputs dummy empty response resp = {"error_code": None, "result": {"id": "PHID-XXXX-YYYYY"}} return ( 201, { "Content-Type": "application/json", "unittest": "full-file-inline" }, json.dumps(resp), ) responses.add_callback( responses.POST, "http://phabricator.test/api/differential.createinline", callback=_check_inline, ) responses.add_callback( responses.POST, "http://phabricator.test/api/differential.createcomment", callback=_check_comment, ) with mock_phabricator as api: revision = Revision(api, mock_try_task) revision.lines = { # Add dummy lines diff "xx.cpp": [123, 124, 125] } revision.files = list(revision.lines.keys()) reporter = PhabricatorReporter(api=api) issue = DefaultIssue( analyzer="full-file-analyzer", revision=revision, path="xx.cpp", line=-1, nb_lines=0, check="a-huge-issue", message="Something bad happened on the whole file !", ) assert issue.line is None # check auto conversion assert str( issue ) == "full-file-analyzer issue a-huge-issue@error xx.cpp full file" assert issue.is_publishable() assert revision.has_file(issue.path) assert revision.contains(issue) issues, patches = reporter.publish([issue], revision, []) assert len(issues) == 1 assert len(patches) == 0 # Check the comment 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") == "full-file-comment" # Check the inline callback has been used call = responses.calls[-2] assert call.request.url == "http://phabricator.test/api/differential.createinline" assert call.response.headers.get("unittest") == "full-file-inline"
def test_phabricator_clang_tidy(mock_phabricator, mock_try_task): """ Test Phabricator reporter publication on a mock clang-tidy issue """ from code_review_bot.report.phabricator import PhabricatorReporter from code_review_bot.revisions import Revision from code_review_bot.tasks.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 = Revision(api, mock_try_task) revision.lines = { # Add dummy lines diff "another_test.cpp": [41, 42, 43] } revision.files = ["another_test.cpp"] reporter = PhabricatorReporter( { "analyzers": ["clang-tidy"], "modes": ("comment") }, api=api) issue = ClangTidyIssue( "mock-clang-tidy", revision, "another_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_phabricator_unitresult(mock_phabricator, mock_try_task): """ Test Phabricator UnitResult for a CoverityIssue """ from code_review_bot.tasks.coverity import CoverityIssue from code_review_bot.report.phabricator import PhabricatorReporter from code_review_bot.revisions import Revision 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": [], "unit": [], "type": "work", "__conduit__": { "token": "deadbeef" }, } # Outputs dummy empty response resp = {"error_code": None, "result": None} return ( 201, { "Content-Type": "application/json", "unittest": "coverity" }, json.dumps(resp), ) def _check_unitresult(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": [], "unit": [{ "details": 'Code review bot found a **build error**: \nDereferencing a pointer that might be "nullptr" "env" when calling "lookupImport".', "format": "remarkup", "name": "general", "namespace": "code-review", "result": "fail", }], "type": "fail", "__conduit__": { "token": "deadbeef" }, } # Outputs dummy empty response resp = {"error_code": None, "result": None} return ( 201, { "Content-Type": "application/json", "unittest": "coverity" }, json.dumps(resp), ) # First callback is to catch the publicatio of a lint responses.add_callback( responses.POST, "http://phabricator.test/api/harbormaster.sendmessage", callback=_check_message, ) # Catch the publication of a UnitResult failure responses.add_callback( responses.POST, "http://phabricator.test/api/harbormaster.sendmessage", callback=_check_unitresult, ) with mock_phabricator as api: revision = Revision(api, mock_try_task) revision.lines = { # Add dummy lines diff "test.cpp": [41, 42, 43] } revision.build_target_phid = "PHID-HMBD-deadbeef12456" reporter = PhabricatorReporter( { "analyzers": ["coverity"], "mode": "harbormaster", "publish_build_errors": True, }, api=api, ) issue_dict = { "line": 41, "reliability": "medium", "message": 'Dereferencing a pointer that might be "nullptr" "env" when calling "lookupImport".', "flag": "NULL_RETURNS", "build_error": True, "extra": { "category": "Null pointer dereferences", "stateOnServer": { "ownerLdapServerName": "local", "stream": "Firefox", "cid": 95687, "cached": False, "retrievalDateTime": "2019-05-13T10:20:22+00:00", "firstDetectedDateTime": "2019-04-08T12:57:07+00:00", "presentInReferenceSnapshot": False, "components": ["js"], "customTriage": {}, "triage": { "fixTarget": "Untargeted", "severity": "Unspecified", "classification": "Unclassified", "owner": "try", "legacy": "False", "action": "Undecided", "externalReference": "", }, }, }, } issue = CoverityIssue("mock-coverity", revision, issue_dict, "test.cpp") 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 # Only check the last call print(responses.calls[-1].request.url) assert responses.calls[-1].response.headers.get( "unittest") == "coverity"
def test_phabricator_harbormaster(mock_phabricator, mock_try_task): """ Test Phabricator reporter publication on a mock clang-tidy issue using harbormaster """ from code_review_bot.report.phabricator import PhabricatorReporter from code_review_bot.revisions import Revision from code_review_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 = Revision(api, mock_try_task) 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( "mock-clang-tidy", 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"
def test_phabricator_clang_format(mock_config, mock_phabricator, mock_try_task): """ Test Phabricator reporter publication on a mock clang-format issue """ from code_review_bot.report.phabricator import PhabricatorReporter from code_review_bot.revisions import Revision, ImprovementPatch from code_review_bot.tasks.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 = Revision(api, mock_try_task) revision.lines = { # Add dummy lines diff "test.cpp": [41, 42, 43], "dom/test.cpp": [42], } reporter = PhabricatorReporter({"analyzers": ["clang-format"]}, api=api) issue = ClangFormatIssue("mock-clang-format", "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"