From efd85662312574800628a6cb26b03c5bd9002848 Mon Sep 17 00:00:00 2001 From: csmarton Date: Mon, 4 Sep 2017 21:59:38 +0200 Subject: [PATCH] Show reports with the same bughash in the bugviewer --- api/v6/report_server.thrift | 30 +++++---- .../server/client_db_access_handler.py | 18 ++++-- .../report_viewer_api/test_report_counting.py | 2 +- .../report_viewer_api/test_run_filter.py | 4 +- www/scripts/codecheckerviewer/BugViewer.js | 61 ++++++++++++++++++- .../codecheckerviewer/CheckerStatistics.js | 2 +- www/scripts/codecheckerviewer/ListOfBugs.js | 8 ++- www/scripts/codecheckerviewer/ListOfRuns.js | 14 +++-- www/style/codecheckerviewer.css | 9 ++- 9 files changed, 115 insertions(+), 33 deletions(-) diff --git a/api/v6/report_server.thrift b/api/v6/report_server.thrift index 6b598366c0..37d6365e8d 100644 --- a/api/v6/report_server.thrift +++ b/api/v6/report_server.thrift @@ -25,6 +25,11 @@ struct RunData { } typedef list RunDataList +struct RunFilter { + 1: list runIds, + 2: string name +} + struct RunReportCount { 1: i64 runId, // unique id of the run 2: string name, // human readable name of the run @@ -47,17 +52,18 @@ struct ReviewData { } struct ReportData { - 1: string checkerId, // The qualified name of the checker that reported this. - 2: string bugHash, // This is unique id of the concrete report. - 3: string checkedFile, // This is a filepath, the original main file the analyzer was called with. - 4: string checkerMsg, // Description of the bug report. - 5: i64 reportId, // id of the report in the current run in the db. - 6: i64 fileId, // Unique id of the file the report refers to. - 7: i64 line, // line number or the reports main section (not part of the path). - 8: i64 column, // column number of the report main section (not part of the path). - 9: shared.Severity severity // Checker severity. - 10: ReviewData reviewData // Bug review status information. - 11: shared.DetectionStatus detectionStatus // State of the bug (see the enum constant values). + 1: i64 runId, // Unique id of the run. + 2: string checkerId, // The qualified name of the checker that reported this. + 3: string bugHash, // This is unique id of the concrete report. + 4: string checkedFile, // This is a filepath, the original main file the analyzer was called with. + 5: string checkerMsg, // Description of the bug report. + 6: i64 reportId, // id of the report in the current run in the db. + 7: i64 fileId, // Unique id of the file the report refers to. + 8: i64 line, // line number or the reports main section (not part of the path). + 9: i64 column, // column number of the report main section (not part of the path). + 10: shared.Severity severity // Checker severity. + 11: ReviewData reviewData // Bug review status information. + 12: shared.DetectionStatus detectionStatus // State of the bug (see the enum constant values). } typedef list ReportDataList @@ -184,7 +190,7 @@ service codeCheckerDBAccess { // get the run Ids and dates from the database to select one run // PERMISSION: PRODUCT_ACCESS - RunDataList getRunData(1: string runNameFilter) + RunDataList getRunData(1: RunFilter runFilter) throws (1: shared.RequestFailed requestError), // PERMISSION: PRODUCT_ACCESS diff --git a/libcodechecker/server/client_db_access_handler.py b/libcodechecker/server/client_db_access_handler.py index 7eebad10b5..5a17465616 100644 --- a/libcodechecker/server/client_db_access_handler.py +++ b/libcodechecker/server/client_db_access_handler.py @@ -471,7 +471,7 @@ def __get_run_ids_to_query(self, session, cmp_data=None): return run_ids @timeit - def getRunData(self, run_name_filter): + def getRunData(self, runFilter): self.__require_access() try: session = self.__Session() @@ -485,8 +485,11 @@ def getRunData(self, run_name_filter): q = session.query(Run, stmt.c.report_count) - if run_name_filter is not None: - q = q.filter(Run.name.ilike('%' + run_name_filter + '%')) + if runFilter is not None: + if runFilter.runIds is not None: + q = q.filter(Run.id.in_(runFilter.runIds)) + if runFilter.name is not None: + q = q.filter(Run.name.ilike('%' + runFilter.name + '%')) q = q.outerjoin(stmt, Run.id == stmt.c.run_id) \ .order_by(Run.date) @@ -561,6 +564,7 @@ def getReport(self, reportId): date=None) return ReportData( + runId=report.run_id, bugHash=report.bug_id, checkedFile=source_file.filepath, checkerMsg=report.checker_message, @@ -627,7 +631,8 @@ def getRunResults(self, run_ids, limit, offset, sort_types, date=None) results.append( - ReportData(bugHash=report.bug_id, + ReportData(runId=report.run_id, + bugHash=report.bug_id, checkedFile=source_file.filepath, checkerMsg=report.checker_message, reportId=report.id, @@ -713,7 +718,8 @@ def getRunResults_v2(self, run_ids, limit, offset, sort_types, date=None) results.append( - ReportData(bugHash=report.bug_id, + ReportData(runId=report.run_id, + bugHash=report.bug_id, checkedFile=source_file.filepath, checkerMsg=report.checker_message, reportId=report.id, @@ -1756,6 +1762,7 @@ def __queryDiffResults(self, date=None) results.append(ReportData( + runId=report.run_id, bugHash=report.bug_id, checkedFile=source_file.filepath, checkerMsg=report.checker_message, @@ -1830,6 +1837,7 @@ def __queryDiffResults_v2(self, date=None) results.append(ReportData( + runId=report.run_id, bugHash=report.bug_id, checkedFile=source_file.filepath, checkerMsg=report.checker_message, diff --git a/tests/functional/report_viewer_api/test_report_counting.py b/tests/functional/report_viewer_api/test_report_counting.py index 1b224c4c17..24ba6c223c 100644 --- a/tests/functional/report_viewer_api/test_report_counting.py +++ b/tests/functional/report_viewer_api/test_report_counting.py @@ -527,7 +527,7 @@ def test_all_run_report_counts(self): Run name is randomly generated for all of the test runs. """ - runs = self._cc_client.getRunData('') + runs = self._cc_client.getRunData(None) separate_report_counts = 0 for run in runs: diff --git a/tests/functional/report_viewer_api/test_run_filter.py b/tests/functional/report_viewer_api/test_run_filter.py index c321a9257e..b2fee7d14b 100644 --- a/tests/functional/report_viewer_api/test_run_filter.py +++ b/tests/functional/report_viewer_api/test_run_filter.py @@ -10,6 +10,7 @@ from libtest import env +from codeCheckerDBAccess_v6.ttypes import RunFilter class TestRunFilter(unittest.TestCase): @@ -31,7 +32,8 @@ def setUp(self): def __get_runs(self, run_name_filter=None): """ Helper function to get all run names which belong to this test""" - runs = self._cc_client.getRunData(run_name_filter) + run_filter = RunFilter(name=run_name_filter) + runs = self._cc_client.getRunData(run_filter) return [run for run in runs if run.name in self._run_names] diff --git a/www/scripts/codecheckerviewer/BugViewer.js b/www/scripts/codecheckerviewer/BugViewer.js index e4d5a8cc9f..23431d926f 100644 --- a/www/scripts/codecheckerviewer/BugViewer.js +++ b/www/scripts/codecheckerviewer/BugViewer.js @@ -114,8 +114,62 @@ function (declare, domClass, dom, style, fx, Toggler, on, query, Memory, postCreate : function () { var that = this; - this.filepath = dom.create('div', { class : 'editorHeader' }); - dom.place(this.filepath, this.domNode); + this.header = dom.create('div', { class : 'editorHeader' }, this.domNode); + this.filepath = dom.create('div', { class : 'filepath' }, this.header); + this.sameReportSelectorWrapper = dom.create('div', null, this.header); + this._sameReportSelector = new Select({ + class : 'same-report-selector', + onChange : function (reportId) { + topic.publish('openFile', reportId, this.reportIdToRun[reportId]); + }, + + refreshOptions : function (runData, reportData) { + var self = this; + + //--- Get same reports by bughash in all reports ---// + + var reportFilter = new CC_OBJECTS.ReportFilter_v2(); + reportFilter.reportHash = [reportData.bugHash]; + + var sortMode = new CC_OBJECTS.SortMode(); + sortMode.type = CC_OBJECTS.SortType.FILENAME; + sortMode.ord = CC_OBJECTS.Order.ASC; + + var res = CC_SERVICE.getRunResults_v2( + null, + CC_OBJECTS.MAX_QUERY_SIZE, + 0, + [sortMode], + reportFilter, null); + + var runFilter = new CC_OBJECTS.RunFilter(); + runFilter.runIds = res.map(function (report) { return report.runId; }); + + var runDataSet = CC_SERVICE.getRunData(runFilter); + + this.reportIdToRun = {}; + var options = res.map(function (reportData) { + var filename = reportData.checkedFile.replace(/^.*[\\\/]/, ''); + var run = runDataSet.filter(function (r) { + return r.runId === reportData.runId; + })[0]; + + self.reportIdToRun[reportData.reportId] = run; + + return { + label : run.name + ':' + filename + ':' + reportData.line, + value : reportData.reportId + }; + }); + + this.set('options', options); + this.set('value', reportData.reportId, false); + } + }, this.sameReportSelectorWrapper); + + //--- Same reports by bughash selector ---// + + this._sameReportSelector.refreshOptions(this.runData, this.reportData); this.codeMirror = new CodeMirror(this.domNode, { matchBrackets : true, @@ -314,7 +368,7 @@ function (declare, domClass, dom, style, fx, Toggler, on, query, Memory, var that = this; setTimeout(function () { var fullHeight = parseInt(style.getComputedStyle(that.domNode).height); - var headerHeight = getFullHeight(that.filepath); + var headerHeight = getFullHeight(that.header); that.codeMirror.setSize('100%', (fullHeight - headerHeight) + 'px'); that.codeMirror.refresh(); @@ -863,6 +917,7 @@ function (declare, domClass, dom, style, fx, Toggler, on, query, Memory, constructor : function (args) { dojo.safeMixin(this, args); + var that = this; //--- Bug review status ---// diff --git a/www/scripts/codecheckerviewer/CheckerStatistics.js b/www/scripts/codecheckerviewer/CheckerStatistics.js index 3cbcd1cb77..42765737f9 100644 --- a/www/scripts/codecheckerviewer/CheckerStatistics.js +++ b/www/scripts/codecheckerviewer/CheckerStatistics.js @@ -81,7 +81,7 @@ function (declare, ItemFileWriteStore, Deferred, all, Memory, Observable, loadRunStoreData : function () { var that = this; - CC_SERVICE.getRunData('', function (runs) { + CC_SERVICE.getRunData(null, function (runs) { runs.sort(function (a, b) { if (a.name > b.name) return 1; if (a.name < b.name) return -1; diff --git a/www/scripts/codecheckerviewer/ListOfBugs.js b/www/scripts/codecheckerviewer/ListOfBugs.js index a5ef125c80..72c8964c9b 100644 --- a/www/scripts/codecheckerviewer/ListOfBugs.js +++ b/www/scripts/codecheckerviewer/ListOfBugs.js @@ -223,7 +223,7 @@ function (declare, dom, Deferred, ObjectStore, Store, QueryResults, topic, switch (evt.cell.field) { case 'checkedFile': - topic.publish('openFile', item, this); + topic.publish('openFile', item, this.runData, this); break; case 'checkerId': @@ -304,7 +304,7 @@ function (declare, dom, Deferred, ObjectStore, Store, QueryResults, topic, //--- Events ---// this._openFileTopic = topic.subscribe('openFile', - function (reportData, sender) { + function (reportData, runData, sender) { if (sender && sender !== grid) return; @@ -319,12 +319,14 @@ function (declare, dom, Deferred, ObjectStore, Store, QueryResults, topic, var reportFilters = that._bugFilterView.getReportFilters(); var runResultParam = createRunResultFilterParameter(reportFilters); + if (runData) + runResultParam.runIds = [runData.runId]; var bugViewer = new BugViewer({ title : filename, closable : true, reportData : reportData, - runData : that.runData, + runData : runData ? runData : that.runData, runResultParam : runResultParam, onShow : function () { hashHelper.setStateValue('report', reportData.reportId); diff --git a/www/scripts/codecheckerviewer/ListOfRuns.js b/www/scripts/codecheckerviewer/ListOfRuns.js index 43f41f7b1a..2fc15ea307 100644 --- a/www/scripts/codecheckerviewer/ListOfRuns.js +++ b/www/scripts/codecheckerviewer/ListOfRuns.js @@ -139,7 +139,7 @@ function (declare, domConstruct, ItemFileWriteStore, topic, Dialog, Button, * This function refreshes grid with available run data based on text run * name filter. */ - refreshGrid : function (runNameFilter) { + refreshGrid : function (runFilter) { var that = this; this.store.fetch({ @@ -151,7 +151,7 @@ function (declare, domConstruct, ItemFileWriteStore, topic, Dialog, Button, } }); - CC_SERVICE.getRunData(runNameFilter, function (runDataList) { + CC_SERVICE.getRunData(runFilter, function (runDataList) { that._sortRunData(runDataList); runDataList.forEach(function (item) { that._addRunData(item); @@ -179,10 +179,10 @@ function (declare, domConstruct, ItemFileWriteStore, topic, Dialog, Button, }); }, - _populateRuns : function (runNameFilter) { + _populateRuns : function (runFilter) { var that = this; - CC_SERVICE.getRunData(runNameFilter, function (runDataList) { + CC_SERVICE.getRunData(runFilter, function (runDataList) { that._sortRunData(runDataList); // In Firefox the onLoaded function called immediately before topics @@ -215,9 +215,11 @@ function (declare, domConstruct, ItemFileWriteStore, topic, Dialog, Button, onKeyUp : function (evt) { clearTimeout(this.timer); - var filter = this.get('value'); + var runNameFilter = this.get('value'); this.timer = setTimeout(function () { - that.listOfRunsGrid.refreshGrid(filter); + var runFilter = new CC_OBJECTS.RunFilter(); + runFilter.name = runNameFilter; + that.listOfRunsGrid.refreshGrid(runFilter); }, 500); } }); diff --git a/www/style/codecheckerviewer.css b/www/style/codecheckerviewer.css index 71273dc336..75920257d3 100644 --- a/www/style/codecheckerviewer.css +++ b/www/style/codecheckerviewer.css @@ -201,11 +201,18 @@ html, body { } .editorHeader { - padding: 4px; + padding: 5px; border-bottom: 1px solid lightgrey; font-family: monospace; } +.editorHeader .same-report-selector { + position: absolute; + top: 0; + right: 0; + margin: 2px; +} + .buildActionInfo dt { font-weight: bold; }