Mercurial > bitten > bitten-test
changeset 548:e42f5644ea91
Added files from the bitten-lint commit that I forgot to add (see #379, which should really work now)
author | dfraser |
---|---|
date | Thu, 02 Apr 2009 14:09:55 +0000 |
parents | bd02aae24b54 |
children | cb137f2121b9 |
files | bitten/report/lint.py bitten/report/tests/lint.py bitten/templates/bitten_chart_lint.html bitten/templates/bitten_summary_lint.html |
diffstat | 4 files changed, 377 insertions(+), 0 deletions(-) [+] |
line wrap: on
line diff
new file mode 100644 --- /dev/null +++ b/bitten/report/lint.py @@ -0,0 +1,167 @@ +# -*- coding: utf-8 -*- +# +# Copyright (C) 2007 Jeffrey Kyllo <jkyllo-eatlint@echospiral.com> +# +# Based on code from the Bitten project: +# Copyright (C) 2005-2007 Christopher Lenz <cmlenz@gmx.de> +# Copyright (C) 2007 Edgewall Software +# All rights reserved. +# +# This software is licensed as described in the file COPYING, which +# you should have received as part of this distribution. The terms +# are also available at http://echospiral.com/trac/eatlint/wiki/License. + +__docformat__ = 'restructuredtext en' + +from trac.core import * +from bitten.api import IReportChartGenerator, IReportSummarizer + + +class PyLintChartGenerator(Component): + implements(IReportChartGenerator) + + # IReportChartGenerator methods + + def get_supported_categories(self): + return ['lint'] + + def generate_chart_data(self, req, config, category): + assert category == 'lint' + + db = self.env.get_db_cnx() + cursor = db.cursor() + + #self.log.debug('config.name=\'%s\'' % (config.name,)) + query = """ +select build.rev, + (select count(*) from bitten_report_item as item + where item.report = report.id and item.name='category' and item.value='convention'), + (select count(*) from bitten_report_item as item + where item.report = report.id and item.name='category' and item.value='error'), + (select count(*) from bitten_report_item as item + where item.report = report.id and item.name='category' and item.value='refactor'), + (select count(*) from bitten_report_item as item + where item.report = report.id and item.name='category' and item.value='warning') +from bitten_report as report + left outer join bitten_build as build ON (report.build=build.id) +where build.config='%s' and report.category='lint' +group by build.rev_time, build.rev, build.platform +order by build.rev_time;""" % (config.name,) + + #self.log.debug('sql=\'%s\'' % (query,)) + cursor.execute(query) + + lint = [] + prev_rev = None + prev_counts = None + + for rev, conv, err, ref, warn in cursor: + total = conv + err + ref + warn + curr_counts = [rev, total, conv, err, ref, warn] + if rev != prev_rev: + lint.append(curr_counts) + else: + # cunningly / dubiously set rev = max(rev, rev) along with the counts + lint[-1] = [max(prev, curr) for prev, curr in zip(curr_counts, lint[-1])] + # recalculate total + lint[-1][1] = sum(lint[-1][2:]) + prev_rev = rev + + data = {'title': 'Lint Problems by Type', + 'data': [ + ['Revision'] + ['[%s]' % item[0] for item in lint], + ['Total Problems'] + [item[1] for item in lint], + ['Convention'] + [item[2] for item in lint], + ['Error'] + [item[3] for item in lint], + ['Refactor'] + [item[4] for item in lint], + ['Warning'] + [item[5] for item in lint], + ]} + + return 'bitten_chart_lint.html', data + + +class PyLintSummarizer(Component): + implements(IReportSummarizer) + + # IReportSummarizer methods + + def get_supported_categories(self): + return ['lint'] + + def render_summary(self, req, config, build, step, category): + assert category == 'lint' + + db = self.env.get_db_cnx() + cursor = db.cursor() + cursor.execute(""" +SELECT item_type.value AS type, item_file.value AS file, + item_line.value as line, item_category.value as category, + report.category as report_category +FROM bitten_report AS report + LEFT OUTER JOIN bitten_report_item AS item_type + ON (item_type.report=report.id AND item_type.name='type') + LEFT OUTER JOIN bitten_report_item AS item_file + ON (item_file.report=report.id AND + item_file.item=item_type.item AND + item_file.name='file') + LEFT OUTER JOIN bitten_report_item AS item_line + ON (item_line.report=report.id AND + item_line.item=item_type.item AND + item_line.name='lines') + LEFT OUTER JOIN bitten_report_item AS item_category + ON (item_category.report=report.id AND + item_category.item=item_type.item AND + item_category.name='category') +WHERE report_category='lint' AND build=%s AND step=%s +ORDER BY item_type.value""", (build.id, step.name)) + + file_data = {} + + type_total = {} + category_total = {} + line_total = 0 + file_total = 0 + seen_files = {} + + for type, file, line, category, report_category in cursor: + if not file_data.has_key(file): + file_data[file] = {'file': file, 'type': {}, 'lines': 0, 'category': {}} + + d = file_data[file] + #d = {'type': type, 'line': line, 'category': category} + if not d['type'].has_key(type): + d['type'][type] = 0 + d['type'][type] += 1 + + d['lines'] += 1 + line_total += 1 + + if not d['category'].has_key(category): + d['category'][category] = 0 + d['category'][category] += 1 + + if file: + d['href'] = req.href.browser(config.path, file) + + if not type_total.has_key(type): + type_total[type] = 0 + type_total[type] += 1 + + if not category_total.has_key(category): + category_total[category] = 0 + category_total[category] += 1 + + if not seen_files.has_key(file): + seen_files[file] = 0 + file_total += 1 + + data = [] + for d in file_data.values(): + d['catnames'] = d['category'].keys() + data.append(d) + + template_data = {} + template_data['data'] = data + template_data['totals'] = {'type': type_total, 'category': category_total, 'files': file_total, 'lines': line_total} + + return 'bitten_summary_lint.html', template_data
new file mode 100644 --- /dev/null +++ b/bitten/report/tests/lint.py @@ -0,0 +1,125 @@ +# -*- coding: utf-8 -*- +# +# Copyright (C) 2005-2007 Christopher Lenz <cmlenz@gmx.de> +# Copyright (C) 2007 Edgewall Software +# All rights reserved. +# +# This software is licensed as described in the file COPYING, which +# you should have received as part of this distribution. The terms +# are also available at http://bitten.edgewall.org/wiki/License. + +import unittest + +from trac.db import DatabaseManager +from trac.test import EnvironmentStub, Mock +from bitten.model import * +from bitten.report.lint import PyLintChartGenerator + + +class PyLintChartGeneratorTestCase(unittest.TestCase): + + def setUp(self): + self.env = EnvironmentStub() + self.env.path = '' + + db = self.env.get_db_cnx() + cursor = db.cursor() + connector, _ = DatabaseManager(self.env)._get_connector() + for table in schema: + for stmt in connector.to_sql(table): + cursor.execute(stmt) + + def test_supported_categories(self): + generator = PyLintChartGenerator(self.env) + self.assertEqual(['lint'], generator.get_supported_categories()) + + def test_no_reports(self): + req = Mock() + config = Mock(name='trunk') + generator = PyLintChartGenerator(self.env) + template, data = generator.generate_chart_data(req, config, 'lint') + self.assertEqual('bitten_chart_lint.html', template) + self.assertEqual('Lint Problems by Type', data['title']) + self.assertEqual('Revision', data['data'][0][0]) + self.assertEqual('Total Problems', data['data'][1][0]) + self.assertEqual('Convention', data['data'][2][0]) + self.assertEqual('Error', data['data'][3][0]) + self.assertEqual('Refactor', data['data'][4][0]) + self.assertEqual('Warning', data['data'][5][0]) + + def test_single_platform(self): + config = Mock(name='trunk') + build = Build(self.env, config='trunk', platform=1, rev=123, + rev_time=42) + build.insert() + report = Report(self.env, build=build.id, step='foo', category='lint') + report.items += [{'category': 'convention'}, {'category': 'warning'}, + {'category': 'error'}, {'category': 'refactor'}, + {'category': 'warning'}, {'category': 'error'}, + {'category': 'refactor'}, {'category': 'error'}, + {'category': 'refactor'}, {'category': 'refactor'}] + report.insert() + + req = Mock() + generator = PyLintChartGenerator(self.env) + template, data = generator.generate_chart_data(req, config, 'lint') + self.assertEqual('bitten_chart_lint.html', template) + self.assertEqual('Lint Problems by Type', data['title']) + self.assertEqual('Revision', data['data'][0][0]) + self.assertEqual('[123]', data['data'][0][1]) + + self.assertEqual('Total Problems', data['data'][1][0]) + self.assertEqual(10, data['data'][1][1]) + self.assertEqual('Convention', data['data'][2][0]) + self.assertEqual(1, data['data'][2][1]) + self.assertEqual('Error', data['data'][3][0]) + self.assertEqual(3, data['data'][3][1]) + self.assertEqual('Refactor', data['data'][4][0]) + self.assertEqual(4, data['data'][4][1]) + self.assertEqual('Warning', data['data'][5][0]) + self.assertEqual(2, data['data'][5][1]) + + def test_multi_platform(self): + config = Mock(name='trunk') + + build = Build(self.env, config='trunk', platform=1, rev=123, + rev_time=42) + build.insert() + report = Report(self.env, build=build.id, step='foo', category='lint') + report.items += [{'category': 'error'}, {'category': 'refactor'}] + report.insert() + + build = Build(self.env, config='trunk', platform=2, rev=123, + rev_time=42) + build.insert() + report = Report(self.env, build=build.id, step='foo', category='lint') + report.items += [{'category': 'convention'}, {'category': 'warning'}] + report.insert() + + req = Mock() + generator = PyLintChartGenerator(self.env) + template, data = generator.generate_chart_data(req, config, 'lint') + self.assertEqual('bitten_chart_lint.html', template) + self.assertEqual('Lint Problems by Type', data['title']) + self.assertEqual('Revision', data['data'][0][0]) + self.assertEqual('[123]', data['data'][0][1]) + + self.assertEqual('Total Problems', data['data'][1][0]) + self.assertEqual(4, data['data'][1][1]) + self.assertEqual('Convention', data['data'][2][0]) + self.assertEqual(1, data['data'][2][1]) + self.assertEqual('Error', data['data'][3][0]) + self.assertEqual(1, data['data'][3][1]) + self.assertEqual('Refactor', data['data'][4][0]) + self.assertEqual(1, data['data'][4][1]) + self.assertEqual('Warning', data['data'][5][0]) + self.assertEqual(1, data['data'][5][1]) + + +def suite(): + suite = unittest.TestSuite() + suite.addTest(unittest.makeSuite(PyLintChartGeneratorTestCase)) + return suite + +if __name__ == '__main__': + unittest.main(defaultTest='suite')
new file mode 100644 --- /dev/null +++ b/bitten/templates/bitten_chart_lint.html @@ -0,0 +1,39 @@ +<chart xmlns:py="http://genshi.edgewall.org/"> + + <chart_type> + <value>area</value> + <value>line</value> + <value>line</value> + <value>line</value> + <value>line</value> + </chart_type> + + <axis_category size="10" orientation="diagonal_up" + skip="${len(data[0]) / 6}"/> + <axis_ticks value_ticks="false" category_ticks="true" major_thickness="1" + minor_thickness="0" major_color="000000" position="outside"/> + + <chart_data> + <row py:for="idx, row in enumerate(data)"> + <py:choose py:for="jdx, value in enumerate(row)"> + <string py:when="not idx or not jdx">$value</string> + <number py:otherwise="">$value</number> + </py:choose> + </row> + </chart_data> + + <chart_border color="999999" left_thickness="1" bottom_thickness="1"/> + <chart_grid_h alpha="5" color="666666" thickness="3"/> + <chart_pref line_thickness="2" point_shape="none"/> + <chart_value position="cursor"/> + <series_color> + </series_color> + + <legend_label layout="vertical" alpha="60"/> + <legend_rect x="60" y="50" width="10"/> + + <draw> + <text width="320" height="40" h_align="center" v_align="bottom" size="12" >$title</text> + </draw> + +</chart>
new file mode 100644 --- /dev/null +++ b/bitten/templates/bitten_summary_lint.html @@ -0,0 +1,46 @@ +<!DOCTYPE html + PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" + "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd"> +<html xmlns="http://www.w3.org/1999/xhtml" + xmlns:py="http://genshi.edgewall.org/"> + +<h3>Code Lint</h3> +<table class="listing lint"> + <thead> + <tr><th rowspan="2" class="file">File</th><th colspan="4" class="category">Problem Category Totals</th><th rowspan="2" class="total">Total</th></tr> + <tr> + <th class="category">Convention</th> + <th class="category">Refactor</th> + <th class="category">Warning</th> + <th class="category">Error</th> + </tr> + </thead> + + <tbody><py:for each="item in data.data"> + <tr> + <td class="file"> + <a py:if="item.href" href="${item.href}">$item.file</a> + <span py:if="not item.href">$item.file</span> + </td> + + <td class="category">$item.category.convention</td> + <td class="category">$item.category.refactor</td> + <td class="category">$item.category.warning</td> + <td class="category">$item.category.error</td> + <td class="category">$item.lines</td> + + </tr> + </py:for> + </tbody> + <tbody class="totals" py:with="totals = data.totals"><tr> + <th>Total (in $totals.files files)</th> + + <td class="category total">$totals.category.convention</td> + <td class="category total">$totals.category.refactor</td> + <td class="category total">$totals.category.warning</td> + <td class="category total">$totals.category.error</td> + <td class="total">$totals.lines</td> + </tr></tbody> +</table> + +</html>