diff --git a/analyzer/tests/functional/analyze_and_parse/test_analyze_and_parse.py b/analyzer/tests/functional/analyze_and_parse/test_analyze_and_parse.py index 042695570c..0ae06f0c0a 100644 --- a/analyzer/tests/functional/analyze_and_parse/test_analyze_and_parse.py +++ b/analyzer/tests/functional/analyze_and_parse/test_analyze_and_parse.py @@ -766,9 +766,8 @@ def test_html_checker_url(self): content = f.read() self.assertTrue(re.search( - 'UNKNOWN CHECKER NAME', content)) + '"checker-url": "https://.*alpha-clone-clonechecker', content)) + self.assertTrue(re.search('"checker-url": ""', content)) self.assertTrue(re.search('UNKNOWN CHECKER NAME', content)) # Test whether documentation urls are set properly for known @@ -785,4 +784,4 @@ def test_html_checker_url(self): with open(report_html, 'r', encoding="utf-8", errors="ignore") as f: content = f.read() - self.assertTrue(re.search('"url": null', content)) + self.assertTrue(re.search('"url": ""', content)) diff --git a/tools/report-converter/codechecker_report_converter/report/output/html/html.py b/tools/report-converter/codechecker_report_converter/report/output/html/html.py index fd4ef21342..59e7efe212 100644 --- a/tools/report-converter/codechecker_report_converter/report/output/html/html.py +++ b/tools/report-converter/codechecker_report_converter/report/output/html/html.py @@ -240,7 +240,7 @@ def to_macro_expansions( 'reportHash': report.report_hash, 'checker': { 'name': report.checker_name, - 'url': self._get_doc_url(report) + 'url': self._get_doc_url(report) or '' }, 'analyzerName': report.analyzer_name, 'line': report.line, @@ -302,82 +302,22 @@ def create_index_html(self, output_dir: str): for report in reports: html_report_links.append({'link': html_file, 'report': report}) - html_report_links.sort( - key=lambda data: self.files[data['report']['fileId']]['filePath']) - - with io.StringIO() as table_reports: - # Create table header. - table_reports.write(''' - -   - File - Severity - Checker name - Message - Bug path length - Review status - ''') - - # Create table lines. - for i, data in enumerate(html_report_links): - html_file = os.path.basename(data['link']) - report = data['report'] - - severity = report['severity'].lower() \ - if 'severity' in report \ - and report['severity'] is not None \ - else '' - - review_status = report['reviewStatus'] \ - if 'reviewStatus' in report and \ - report['reviewStatus'] is not None \ - else '' - - events = report['events'] - if events: - line = events[-1]['line'] - message = events[-1]['message'] - bug_path_length = len(events) - else: - line = report['line'] - message = report['message'] - bug_path_length = 1 - - rs = review_status.lower().replace(' ', '-') - file_path = self.files[report['fileId']]['filePath'] - - checker = report['checker'] - doc_url = checker.get('url') - if doc_url: - checker_name_col_content = f'{checker["name"]}' - else: - checker_name_col_content = checker["name"] - - table_reports.write(f''' - - {i + 1} - - - {file_path} @ Line {line} - - - - - - {checker_name_col_content} - {message} - {bug_path_length} - - {review_status} - - ''') - - substitute_data = self._tag_contents - substitute_data.update({'table_reports': table_reports.getvalue()}) - - content = self._index.substitute(substitute_data) + table_reports = map(lambda data: { + 'link': data['link'], + 'file-path': data['report']['fileId'], + 'report-hash': data['report']['reportHash'], + 'checker-name': data['report']['checker']['name'], + 'checker-url': data['report']['checker']['url'], + 'line': data['report']['line'], + 'message': data['report']['message'], + 'review-status': data['report']['reviewStatus'], + 'severity': data['report']['severity'], + 'bug-path-length': len(data['report']['events']) + }, html_report_links) + + self._tag_contents['table_reports'] = json.dumps(list(table_reports)) + + content = self._index.substitute(self._tag_contents) output_path = os.path.join(output_dir, 'index.html') with open(output_path, 'w+', encoding='utf-8', errors='replace') as html_output: diff --git a/tools/report-converter/codechecker_report_converter/report/output/html/static/css/buglist.css b/tools/report-converter/codechecker_report_converter/report/output/html/static/css/buglist.css index 1624112ad4..a3656400e4 100644 --- a/tools/report-converter/codechecker_report_converter/report/output/html/static/css/buglist.css +++ b/tools/report-converter/codechecker_report_converter/report/output/html/static/css/buglist.css @@ -1,4 +1,4 @@ -#report-list { +#report-list-table { width: 100%; } diff --git a/tools/report-converter/codechecker_report_converter/report/output/html/static/index.html b/tools/report-converter/codechecker_report_converter/report/output/html/static/index.html index 507495538c..d91dc177fe 100644 --- a/tools/report-converter/codechecker_report_converter/report/output/html/static/index.html +++ b/tools/report-converter/codechecker_report_converter/report/output/html/static/index.html @@ -16,11 +16,13 @@ ${browser_support} ${bug_list} + var reports = ${table_reports}; + window.onload = function () { if (!browserCompatible) { setNonCompatibleBrowserMessage(); } else { - BugList.init() + BugList.initByUrl(); } } @@ -28,9 +30,31 @@
Go To Statistics - - ${table_reports} +
+ + + + + + + + + + + + +
 FileSeverityChecker nameMessageBug path lengthReview status
+ + + + + +
diff --git a/tools/report-converter/codechecker_report_converter/report/output/html/static/js/buglist.js b/tools/report-converter/codechecker_report_converter/report/output/html/static/js/buglist.js index 09d84c4856..951c195cd9 100644 --- a/tools/report-converter/codechecker_report_converter/report/output/html/static/js/buglist.js +++ b/tools/report-converter/codechecker_report_converter/report/output/html/static/js/buglist.js @@ -6,23 +6,35 @@ var BugList = { - init : function () { - this.initTableSort(); - this.initBugPathLength(); - this.initByUrl(); + _urlHash : function () { + var state = {}; + window.location.hash.substring(1).split('&').forEach(function (s) { + var parts = s.split('='); + state[parts[0]] = parts[1]; + }); + return state; + }, + + _cmp3 : function (a, b) { + return a < b ? -1 : a > b ? 1 : 0; }, initTableSort : function () { var that = this; - var table = document.getElementById('report-list'); + var table = document.getElementById('report-list-table'); table.querySelectorAll('th').forEach(function (column) { - if (that.canSort(column.id)) { - column.addEventListener('click', function () { - that.sort(column.id); - }); - column.classList.add('sortable'); - } + column.addEventListener('click', function () { + var state = that._urlHash(); + + var asc = state.sort === column.id + ? !parseInt(state.asc) + : !!parseInt(state.asc); + + that.sort(column.id, asc); + that.load(); + }); + column.classList.add('sortable'); }); }, @@ -37,15 +49,12 @@ var BugList = { }, initByUrl : function () { - var state = {}; - window.location.hash.substr(1).split('&').forEach(function (s) { - var parts = s.split('='); - state[parts[0]] = parts[1]; - }); - + var state = this._urlHash(); var column = state['sort'] ? state['sort'] : 'file-path'; var asc = state['asc'] ? !!parseInt(state['asc']) : true; + this.initTableSort(); this.sort(column, asc); + this.load(); }, generateRedGreenGradientColor : function (value, max, opacity) { @@ -56,98 +65,133 @@ var BugList = { + ',' + opacity + ')'; }, - canSort : function (columnId) { - return columnId === 'report-id' || - columnId === 'file-path' || - columnId === 'severity' || - columnId === 'checker-name' || - columnId === 'message' || - columnId === 'bug-path-length' || - columnId === 'review-status'; - }, + sort : function (columnId, asc) { + var that = this; + + var severities = ['STYLE', 'LOW', 'MEDIUM', 'HIGH', 'CRITICAL']; + + function compare(a, b) { + var result; + + if (columnId == 'file-path') + result = that._cmp3( + [a['file-path'], a['line']], + [b['file-path'], b['line']]); + + if (columnId == 'severity') + result = that._cmp3( + severities.indexOf(a['severity']), + severities.indexOf(b['severity'])); - compare : function (columnId, a, b, asc) { - switch (columnId) { - case 'report-id': - case 'bug-path-length': - return asc - ? parseInt(a.innerHTML) > parseInt(b.innerHTML) - : parseInt(a.innerHTML) < parseInt(b.innerHTML); - - case 'file-path': - var fileA = a.getAttribute('file'); - var fileB = b.getAttribute('file'); - var lineA = parseInt(a.getAttribute('line')); - var lineB = parseInt(b.getAttribute('line')); - - if (asc) { - if (fileA > fileB) { - return true; - } else if (fileA === fileB) { - return lineA > lineB ? true : false; - } else { - return false; - } - } else { - if (fileA < fileB) { - return true; - } else if (fileA === fileB) { - return lineA < lineB ? true : false; - } else { - return false; - } - } - - case 'severity': - return asc - ? a.getAttribute('severity') > b.getAttribute('severity') - : a.getAttribute('severity') < b.getAttribute('severity'); - - default: - return asc - ? a.innerHTML.toLowerCase() > b.innerHTML.toLowerCase() - : a.innerHTML.toLowerCase() < b.innerHTML.toLowerCase(); + result = that._cmp3(a[columnId], b[columnId]); + + return asc ? result : -result; } + + reports.sort(compare); + window.location.hash = '#sort=' + columnId + '&asc=' + (asc ? 1 : 0); }, - sort : function (columnId, asc) { - var rows = null, - switching = true, - i, j, x, y, minIdx; + buildRow : function (data, id) { + let row = document.createElement('tr'); + + let id_col = document.createElement('td'); + id_col.appendChild(document.createTextNode(id)); + row.appendChild(id_col); + + let file_col = document.createElement('td'); + let file_col_a = document.createElement('a'); + file_col_a.setAttribute( + 'href', data.link + '#reportHash=' + data['report-hash']); + file_col_a.innerHTML = data['file-path'] + ' @ Line ' + data.line; + file_col.appendChild(file_col_a); + row.appendChild(file_col); + + let severity_col = document.createElement('td'); + severity_col.setAttribute('class', 'severity'); + let severity_col_i = document.createElement('i'); + severity_col_i.setAttribute( + 'class', 'severity-' + data.severity.toLowerCase()); + severity_col_i.setAttribute('title', data.severity); + severity_col.appendChild(severity_col_i); + row.appendChild(severity_col); + + let checker_col = document.createElement('td'); + let checker_col_a = document.createElement('a'); + checker_col_a.setAttribute('href', data['checker-url']); + checker_col_a.setAttribute('target', '_blank'); + checker_col_a.appendChild( + document.createTextNode(data['checker-name'])); + checker_col.appendChild(checker_col_a); + row.appendChild(checker_col); + + let message_col = document.createElement('td'); + message_col.appendChild(document.createTextNode(data.message)); + row.appendChild(message_col); + + let length_col = document.createElement('td'); + length_col.setAttribute('class', 'bug-path-length'); + length_col.appendChild(document.createTextNode(data['bug-path-length'])); + row.appendChild(length_col); + + let review_col = document.createElement('td'); + review_col.appendChild(document.createTextNode(data['review-status'])); + row.appendChild(review_col); + + return row; + }, - var table = document.getElementById('report-list'); - var column = document.getElementById(columnId); - var cellIndex = column.cellIndex; + loadTable : function (pageNo, pageSize) { + var startIdx = (pageNo - 1) * pageSize; + var endIdx = Math.min(startIdx + pageSize, reports.length); - if (asc === undefined) { - asc = column.getAttribute('sort') === 'desc' ? false : true; - } + var report_list = document.getElementById('report-list'); + report_list.innerHTML = ''; + + for (var i = startIdx; i < endIdx; ++i) + report_list.appendChild(this.buildRow(reports[i], i + 1)); + + this.initBugPathLength(); + }, + + loadPageNumber : function () { + var pageSize = parseInt(document.getElementById('page-size').value); + var pageCount = Math.ceil(reports.length / pageSize); - var n = table.rows.length; - for (i = 1; i < n - 1; i++) - { - minIdx = i; - for (j = i + 1; j < n; j++) { - x = table.rows[minIdx].getElementsByTagName('td')[cellIndex]; - y = table.rows[j].getElementsByTagName('td')[cellIndex]; - if (this.compare(columnId, x, y, asc)) { - minIdx = j; - } - } - - if (minIdx !== i) { - table.rows[i].parentNode.insertBefore( - table.rows[minIdx], table.rows[i]); - } + var pageNumber = document.getElementById('page-number'); + pageNumber.innerHTML = ''; + + for (var i = 1; i <= pageCount; ++i) { + var option = document.createElement('option'); + option.setAttribute('value', i); + option.innerHTML = i; + pageNumber.appendChild(option); } + }, - table.querySelectorAll('th').forEach(function (column) { - column.removeAttribute('sort'); - column.classList.remove('active'); - }); + selectPage : function (pageNumber) { + var pageSize = parseInt(document.getElementById('page-size').value); + this.loadTable(pageNumber, pageSize); + }, - column.classList.add('active'); - column.setAttribute('sort', asc ? 'desc' : 'asc'); - window.location.hash = '#sort=' + columnId + '&asc=' + (asc ? 1 : 0); + prevPage : function () { + var pageNumber = document.getElementById('page-number'); + pageNumber.value = Math.max(parseInt(pageNumber.value) - 1, 1); + this.selectPage(pageNumber.value); + }, + + nextPage : function () { + var pageNumber = document.getElementById('page-number'); + pageNumber.value = Math.min( + parseInt(pageNumber.value) + 1, + pageNumber.children.length); + this.selectPage(pageNumber.value); + }, + + load : function () { + var pageSize = parseInt(document.getElementById('page-size').value); + + this.loadPageNumber(); + this.loadTable(1, pageSize); } }; diff --git a/tools/report-converter/tests/unit/output/html/plist_to_html_test.py b/tools/report-converter/tests/unit/output/html/plist_to_html_test.py index 68bfa75060..678556bb46 100644 --- a/tools/report-converter/tests/unit/output/html/plist_to_html_test.py +++ b/tools/report-converter/tests/unit/output/html/plist_to_html_test.py @@ -185,10 +185,5 @@ def test_html_for_inclusion(self): output_dir = self.__test_html_builder('inclusion') index_html = os.path.join(output_dir, "index.html") - report_count = 0 with open(index_html, 'r', encoding="utf-8", errors="ignore") as f: - for line in f: - if re.search("