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>
Copyright (C) 2012-2017 Edgewall Software