changeset 503:a7c795920c4a

Merging trac-0.11 branch to trunk. This revision is equivalent to [571].
author wbell
date Mon, 09 Mar 2009 00:46:14 +0000
parents a3bcc4f98187
children e2eef154f1af
files bitten/admin.py bitten/api.py bitten/build/ctools.py bitten/build/tests/ctools.py bitten/htdocs/bitten.css bitten/report/coverage.py bitten/report/testing.py bitten/report/tests/coverage.py bitten/report/tests/testing.py bitten/templates/bitten_admin_configs.cs bitten/templates/bitten_admin_configs.html bitten/templates/bitten_admin_master.cs bitten/templates/bitten_admin_master.html bitten/templates/bitten_build.cs bitten/templates/bitten_build.html bitten/templates/bitten_chart_coverage.cs bitten/templates/bitten_chart_coverage.html bitten/templates/bitten_chart_tests.cs bitten/templates/bitten_chart_tests.html bitten/templates/bitten_config.cs bitten/templates/bitten_config.html bitten/templates/bitten_summary_coverage.cs bitten/templates/bitten_summary_coverage.html bitten/templates/bitten_summary_tests.cs bitten/templates/bitten_summary_tests.html bitten/tests/admin.py bitten/tests/web_ui.py bitten/util/testrunner.py bitten/util/xmlio.py bitten/web_ui.py setup.py trac-0.11/COPYING trac-0.11/ChangeLog trac-0.11/MANIFEST.in trac-0.11/README.txt trac-0.11/bitten/__init__.py trac-0.11/bitten/admin.py trac-0.11/bitten/api.py trac-0.11/bitten/build/__init__.py trac-0.11/bitten/build/api.py trac-0.11/bitten/build/config.py trac-0.11/bitten/build/ctools.py trac-0.11/bitten/build/javatools.py trac-0.11/bitten/build/phptools.py trac-0.11/bitten/build/pythontools.py trac-0.11/bitten/build/shtools.py trac-0.11/bitten/build/svntools.py trac-0.11/bitten/build/tests/__init__.py trac-0.11/bitten/build/tests/api.py trac-0.11/bitten/build/tests/config.py trac-0.11/bitten/build/tests/ctools.py trac-0.11/bitten/build/tests/dummy.py trac-0.11/bitten/build/tests/javatools.py trac-0.11/bitten/build/tests/phptools.py trac-0.11/bitten/build/tests/pythontools.py trac-0.11/bitten/build/tests/xmltools.py trac-0.11/bitten/build/xmltools.py trac-0.11/bitten/htdocs/admin.css trac-0.11/bitten/htdocs/bitten.css trac-0.11/bitten/htdocs/bitten_build.png trac-0.11/bitten/htdocs/bitten_buildf.png trac-0.11/bitten/htdocs/bitten_coverage.css trac-0.11/bitten/htdocs/charts.swf trac-0.11/bitten/htdocs/charts_library/arno.swf trac-0.11/bitten/htdocs/charts_library/arst.swf trac-0.11/bitten/htdocs/charts_library/brfl.swf trac-0.11/bitten/htdocs/charts_library/brno.swf trac-0.11/bitten/htdocs/charts_library/brst.swf trac-0.11/bitten/htdocs/charts_library/cl3d.swf trac-0.11/bitten/htdocs/charts_library/clfl.swf trac-0.11/bitten/htdocs/charts_library/clno.swf trac-0.11/bitten/htdocs/charts_library/clp3.swf trac-0.11/bitten/htdocs/charts_library/cls3.swf trac-0.11/bitten/htdocs/charts_library/clst.swf trac-0.11/bitten/htdocs/charts_library/cnno.swf trac-0.11/bitten/htdocs/charts_library/lnno.swf trac-0.11/bitten/htdocs/charts_library/mxno.swf trac-0.11/bitten/htdocs/charts_library/pi3d.swf trac-0.11/bitten/htdocs/charts_library/pino.swf trac-0.11/bitten/htdocs/charts_library/scno.swf trac-0.11/bitten/htdocs/failure.png trac-0.11/bitten/htdocs/tabset.js trac-0.11/bitten/main.py trac-0.11/bitten/master.py trac-0.11/bitten/model.py trac-0.11/bitten/queue.py trac-0.11/bitten/recipe.py trac-0.11/bitten/report/__init__.py trac-0.11/bitten/report/coverage.py trac-0.11/bitten/report/testing.py trac-0.11/bitten/report/tests/__init__.py trac-0.11/bitten/report/tests/coverage.py trac-0.11/bitten/report/tests/testing.py trac-0.11/bitten/slave.py trac-0.11/bitten/templates/bitten_admin_configs.html trac-0.11/bitten/templates/bitten_admin_master.html trac-0.11/bitten/templates/bitten_build.html trac-0.11/bitten/templates/bitten_chart_coverage.html trac-0.11/bitten/templates/bitten_chart_tests.html trac-0.11/bitten/templates/bitten_config.html trac-0.11/bitten/templates/bitten_summary_coverage.html trac-0.11/bitten/templates/bitten_summary_tests.html trac-0.11/bitten/tests/__init__.py trac-0.11/bitten/tests/admin.py trac-0.11/bitten/tests/master.py trac-0.11/bitten/tests/model.py trac-0.11/bitten/tests/queue.py trac-0.11/bitten/tests/recipe.py trac-0.11/bitten/tests/slave.py trac-0.11/bitten/tests/web_ui.py trac-0.11/bitten/upgrades.py trac-0.11/bitten/util/__init__.py trac-0.11/bitten/util/loc.py trac-0.11/bitten/util/testrunner.py trac-0.11/bitten/util/tests/__init__.py trac-0.11/bitten/util/xmlio.py trac-0.11/bitten/web_ui.py trac-0.11/doc/commands.txt trac-0.11/doc/index.txt trac-0.11/doc/install.txt trac-0.11/doc/logo.pdf trac-0.11/doc/logo.png trac-0.11/doc/logo_small.png trac-0.11/doc/recipes.txt trac-0.11/scripts/proxy.py trac-0.11/setup.cfg trac-0.11/setup.py
diffstat 127 files changed, 1123 insertions(+), 15227 deletions(-) [+]
line wrap: on
line diff
--- a/bitten/admin.py
+++ b/bitten/admin.py
@@ -13,12 +13,8 @@
 import re
 
 from trac.core import *
+from trac.admin import IAdminPanelProvider
 from trac.web.chrome import add_stylesheet
-try:
-    require("TracWebAdmin")
-    from webadmin.web_ui import IAdminPageProvider
-except DistributionNotFound, ImportError:
-    IAdminPageProvider = None
 
 from bitten.model import BuildConfig, TargetPlatform
 from bitten.recipe import Recipe, InvalidRecipeError
@@ -28,15 +24,15 @@
 class BuildMasterAdminPageProvider(Component):
     """Web administration panel for configuring the build master."""
 
-    implements(IAdminPageProvider)
+    implements(IAdminPanelProvider)
 
-    # IAdminPageProvider methods
+    # IAdminPanelProvider methods
 
-    def get_admin_pages(self, req):
+    def get_admin_panels(self, req):
         if req.perm.has_permission('BUILD_ADMIN'):
             yield ('bitten', 'Builds', 'master', 'Master Settings')
 
-    def process_admin_request(self, req, cat, page, path_info):
+    def render_admin_panel(self, req, cat, page, path_info):
         from bitten.master import BuildMaster
         master = BuildMaster(self.env)
 
@@ -44,14 +40,9 @@
             self._save_config_changes(req, master)
             req.redirect(req.abs_href.admin(cat, page))
 
-        req.hdf['admin.master'] = {
-            'build_all': master.build_all,
-            'adjust_timestamps': master.adjust_timestamps,
-            'stabilize_wait': master.stabilize_wait,
-            'slave_timeout': master.slave_timeout,
-        }
+        data = {'master': master}
         add_stylesheet(req, 'bitten/admin.css')
-        return 'bitten_admin_master.cs', None
+        return 'bitten_admin_master.html', data
 
     # Internal methods
 
@@ -89,15 +80,15 @@
 class BuildConfigurationsAdminPageProvider(Component):
     """Web administration panel for configuring the build master."""
 
-    implements(IAdminPageProvider)
+    implements(IAdminPanelProvider)
 
-    # IAdminPageProvider methods
+    # IAdminPanelProvider methods
 
-    def get_admin_pages(self, req):
+    def get_admin_panels(self, req):
         if req.perm.has_permission('BUILD_MODIFY'):
             yield ('bitten', 'Builds', 'configs', 'Configurations')
 
-    def process_admin_request(self, req, cat, page, path_info):
+    def render_admin_panel(self, req, cat, page, path_info):
         data = {}
 
         # Analyze url
@@ -108,7 +99,8 @@
             platform_id = None
 
         if config_name: # Existing build config
-            if platform_id or ( # Editing or creating one of the config's target platforms
+            if platform_id or (
+                    # Editing or creating one of the config's target platforms
                     req.method == 'POST' and 'new' in req.args):
 
                 if platform_id: # Editing target platform
@@ -202,9 +194,8 @@
                 })
             data['configs'] = configs
 
-        req.hdf['admin'] = data
         add_stylesheet(req, 'bitten/admin.css')
-        return 'bitten_admin_configs.cs', None
+        return 'bitten_admin_configs.html', data
 
     # Internal methods
 
--- a/bitten/api.py
+++ b/bitten/api.py
@@ -78,8 +78,11 @@
         """
 
     def render_summary(req, config, build, step, category):
-        """Render a summary for the given report and return the resulting HTML
-        as string.
+        """Render a summary for the given report.
+        
+        This function should return a tuple of the form `(template, data)`,
+        where `template` is the name of the template to use and `data` is the
+        data to be passed to the template.
         
         :param req: the request object
         :param config: the build configuration
@@ -94,7 +97,7 @@
 
 
 class IReportChartGenerator(Interface):
-    """Extension point interface for components that generator a chart for a
+    """Extension point interface for components that generate a chart for a
     set of reports."""
 
     def get_supported_categories():
@@ -105,8 +108,9 @@
     def generate_chart_data(req, config, category):
         """Generate the data for a report chart.
         
-        This method should store the data in the HDF of the request and return
-        the name of the template that should process the data.
+        This function should return a tuple of the form `(template, data)`,
+        where `template` is the name of the template to use and `data` is the
+        data to be passed to the template.
         
         :param req: the request object
         :param config: the build configuration
--- a/bitten/build/ctools.py
+++ b/bitten/build/ctools.py
@@ -201,7 +201,82 @@
         print e
         log.warning('Error parsing CppUnit results file (%s)', e)
 
-def gcov(ctxt, include=None, exclude=None, prefix=None):
+def cunit (ctxt, file_=None, srcdir=None):
+    """Collect CUnit XML data.
+    
+    :param ctxt: the build context
+    :type ctxt: `Context`
+    :param file\_: path of the file containing the CUnit results; may contain
+                  globbing wildcards to match multiple files
+    :param srcdir: name of the directory containing the source files, used to
+                   link the test results to the corresponding files
+    """
+    assert file_, 'Missing required attribute "file"'
+
+    try:
+        fileobj = file(ctxt.resolve(file_), 'r')
+        try:
+            total, failed = 0, 0
+            results = xmlio.Fragment()
+            log_elem = xmlio.Fragment()
+            def info (msg):
+                log.info (msg)
+                log_elem.append (xmlio.Element ('message', level='info')[msg])
+            def warning (msg):
+                log.warning (msg)
+                log_elem.append (xmlio.Element ('message', level='warning')[msg])
+            def error (msg):
+                log.error (msg)
+                log_elem.append (xmlio.Element ('message', level='error')[msg])
+            for node in xmlio.parse(fileobj):
+                if node.name != 'CUNIT_RESULT_LISTING':
+                    continue
+                for suiteRun in node.children ('CUNIT_RUN_SUITE'):
+                    for suite in suiteRun.children():
+                        if suite.name not in ('CUNIT_RUN_SUITE_SUCCESS', 'CUNIT_RUN_SUITE_FAILURE'):
+                            warning ("Unknown node: %s" % suite.name)
+                            continue
+                        suiteName = suite.children ('SUITE_NAME').next().gettext()
+                        info ("%s [%s]" % ("*" * (57 - len (suiteName)), suiteName))
+                        for record in suite.children ('CUNIT_RUN_TEST_RECORD'):
+                            for result in record.children():
+                                if result.name not in ('CUNIT_RUN_TEST_SUCCESS', 'CUNIT_RUN_TEST_FAILURE'):
+                                    continue
+                                testName = result.children ('TEST_NAME').next().gettext()
+                                info ("Running %s..." % testName);
+                                test = xmlio.Element('test')
+                                test.attr['fixture'] = suiteName
+                                test.attr['name'] = testName
+                                if result.name == 'CUNIT_RUN_TEST_FAILURE':
+                                    error ("%s(%d): %s"
+                                               % (result.children ('FILE_NAME').next().gettext(),
+                                                  int (result.children ('LINE_NUMBER').next().gettext()),
+                                                  result.children ('CONDITION').next().gettext()))
+                                    test.attr['status'] = 'failure'
+                                    failed += 1
+                                else:
+                                    test.attr['status'] = 'success'
+
+                                results.append(test)
+                                total += 1
+
+            if failed:
+                ctxt.error('%d of %d test%s failed' % (failed, total,
+                           total != 1 and 's' or ''))
+
+            ctxt.report('test', results)
+            ctxt.log (log_elem)
+
+        finally:
+            fileobj.close()
+
+    except IOError, e:
+        log.warning('Error opening CUnit results file (%s)', e)
+    except xmlio.ParseError, e:
+        print e
+        log.warning('Error parsing CUnit results file (%s)', e)
+
+def gcov(ctxt, include=None, exclude=None, prefix=None, root=""):
     """Run ``gcov`` to extract coverage data where available.
     
     :param ctxt: the build context
@@ -210,8 +285,10 @@
     :param exclude: patterns of files and directories that should be excluded
     :param prefix: optional prefix name that is added to object files by the
                    build system
+    :param root: optional root path in which the build system puts the object
+                 files
     """
-    file_re = re.compile(r'^File \`(?P<file>[^\']+)\'\s*$')
+    file_re = re.compile(r'^File (?:\'|\`)(?P<file>[^\']+)\'\s*$')
     lines_re = re.compile(r'^Lines executed:(?P<cov>\d+\.\d+)\% of (?P<num>\d+)\s*$')
 
     files = []
@@ -220,24 +297,35 @@
             files.append(filename)
 
     coverage = xmlio.Fragment()
+    log_elem = xmlio.Fragment()
+    def info (msg):
+        log.info (msg)
+        log_elem.append (xmlio.Element ('message', level='info')[msg])
+    def warning (msg):
+        log.warning (msg)
+        log_elem.append (xmlio.Element ('message', level='warning')[msg])
+    def error (msg):
+        log.error (msg)
+        log_elem.append (xmlio.Element ('message', level='error')[msg])
 
     for srcfile in files:
         # Determine the coverage for each source file by looking for a .gcno
         # and .gcda pair
+        info ("Getting coverage info for %s" % srcfile)
         filepath, filename = os.path.split(srcfile)
         stem = os.path.splitext(filename)[0]
         if prefix is not None:
             stem = prefix + '-' + stem
 
-        objfile = os.path.join(filepath, stem + '.o')
+        objfile = os.path.join (root, filepath, stem + '.o')
         if not os.path.isfile(ctxt.resolve(objfile)):
-            log.warn('No object file found for %s at %s', srcfile, objfile)
+            warning ('No object file found for %s at %s' % (srcfile, objfile))
             continue
-        if not os.path.isfile(ctxt.resolve(stem + '.gcno')):
-            log.warn('No .gcno file found for %s', srcfile)
+        if not os.path.isfile (ctxt.resolve (os.path.join (root, filepath, stem + '.gcno'))):
+            warning ('No .gcno file found for %s at %s' % (srcfile, os.path.join (root, filepath, stem + '.gcno')))
             continue
-        if not os.path.isfile(ctxt.resolve(stem + '.gcda')):
-            log.warn('No .gcda file found for %s', srcfile)
+        if not os.path.isfile (ctxt.resolve (os.path.join (root, filepath, stem + '.gcda'))):
+            warning ('No .gcda file found for %s at %s' % (srcfile, os.path.join (root, filepath, stem + '.gcda')))
             continue
 
         num_lines, num_covered = 0, 0
@@ -274,3 +362,4 @@
         coverage.append(module)
 
     ctxt.report('coverage', coverage)
+    ctxt.log (log_elem)
--- a/bitten/build/tests/ctools.py
+++ b/bitten/build/tests/ctools.py
@@ -107,6 +107,8 @@
         ctools.CommandLine = dummy.CommandLine()
         ctools.gcov(self.ctxt)
         type, category, generator, xml = self.ctxt.output.pop()
+        self.assertEqual('log', type)
+        type, category, generator, xml = self.ctxt.output.pop()
         self.assertEqual('report', type)
         self.assertEqual('coverage', category)
         self.assertEqual(0, len(xml.children))
@@ -131,6 +133,8 @@
 """)
         ctools.gcov(self.ctxt)
         type, category, generator, xml = self.ctxt.output.pop()
+        self.assertEqual('log', type)
+        type, category, generator, xml = self.ctxt.output.pop()
         self.assertEqual('report', type)
         self.assertEqual('coverage', category)
         self.assertEqual(1, len(xml.children))
--- a/bitten/htdocs/bitten.css
+++ b/bitten/htdocs/bitten.css
@@ -157,6 +157,7 @@
 }
 #content.build table.tests tr.failed { background: #d99; }
 #content.build table.tests tr.failed td { font-weight: bold; }
+#content.build table.tests tr.failed:hover th,
 #content.build table.tests tr.failed:hover td,
 #content.build table.tests tr.failed:hover tr { background: #966; }
 #content.build table.tests tr.failed :link,
--- a/bitten/report/coverage.py
+++ b/bitten/report/coverage.py
@@ -58,14 +58,14 @@
                 coverage[-1][2] = int(cov)
             prev_rev = rev
 
-        req.hdf['chart.title'] = 'Test Coverage'
-        req.hdf['chart.data'] = [
-            [''] + ['[%s]' % item[0] for item in coverage],
-            ['Lines of code'] + [item[1] for item in coverage],
-            ['Coverage'] + [int(item[2]) for item in coverage]
-        ]
+        data = {'title': 'Test Coverage',
+                'data': [
+                    [''] + ['[%s]' % item[0] for item in coverage],
+                    ['Lines of code'] + [item[1] for item in coverage],
+                    ['Coverage'] + [int(item[2]) for item in coverage]
+                ]}
 
-        return 'bitten_chart_coverage.cs'
+        return 'bitten_chart_coverage.html', data
 
 
 class TestCoverageSummarizer(Component):
@@ -101,7 +101,7 @@
 GROUP BY file, item_name.value
 ORDER BY item_name.value""", (build.id, step.name))
 
-        data = []
+        units = []
         total_loc, total_cov = 0, 0
         for unit, file, loc, cov in cursor:
             try:
@@ -112,7 +112,7 @@
                 d = {'name': unit, 'loc': loc, 'cov': int(cov)}
                 if file:
                     d['href'] = req.href.browser(config.path, file, rev=build.rev, annotate='coverage')
-                data.append(d)
+                units.append(d)
                 total_loc += loc
                 total_cov += loc * cov
 
@@ -120,10 +120,10 @@
         if total_loc != 0:
             coverage = total_cov // total_loc
 
-        hdf = HDFWrapper(loadpaths=Chrome(self.env).get_all_templates_dirs())
-        hdf['data'] = data
-        hdf['totals'] = {'loc': total_loc, 'cov': int(coverage)}
-        return hdf.render('bitten_summary_coverage.cs')
+        return 'bitten_summary_coverage.html', {
+            'units': units,
+            'totals': {'loc': total_loc, 'cov': int(coverage)}
+        }
 
 
 # Coverage annotation requires the new interface from 0.11
--- a/bitten/report/testing.py
+++ b/bitten/report/testing.py
@@ -56,14 +56,14 @@
             if status != 'success':
                 tests[-1][2] = max(num, tests[-1][2])
 
-        req.hdf['chart.title'] = 'Unit Tests'
-        req.hdf['chart.data'] = [
-            [''] + ['[%s]' % item[0] for item in tests],
-            ['Total'] + [item[1] for item in tests],
-            ['Failures'] + [item[2] for item in tests]
-        ]
+        data = {'title': 'Unit Tests',
+                'data': [
+                    [''] + ['[%s]' % item[0] for item in tests],
+                    ['Total'] + [item[1] for item in tests],
+                    ['Failures'] + [item[2] for item in tests]
+                ]}
 
-        return 'bitten_chart_tests.cs'
+        return 'bitten_chart_tests.html', data
 
 
 class TestResultsSummarizer(Component):
@@ -103,19 +103,20 @@
 GROUP BY file, fixture
 ORDER BY fixture""", (build.id, step.name))
 
-        data = []
+        fixtures = []
         total_success, total_failure, total_error = 0, 0, 0
         for fixture, file, num_success, num_failure, num_error in cursor:
-            data.append({'name': fixture, 'num_success': num_success,
-                         'num_error': num_error, 'num_failure': num_failure})
+            fixtures.append({'name': fixture, 'num_success': num_success,
+                             'num_error': num_error,
+                             'num_failure': num_failure})
             total_success += num_success
             total_failure += num_failure
             total_error += num_error
             if file:
-                data[-1]['href'] = req.href.browser(config.path, file)
+                fixtures[-1]['href'] = req.href.browser(config.path, file)
 
-        hdf = HDFWrapper(loadpaths=Chrome(self.env).get_all_templates_dirs())
-        hdf['data'] = data
-        hdf['totals'] = {'success': total_success, 'failure': total_failure,
-                         'error': total_error}
-        return hdf.render('bitten_summary_tests.cs')
+        data = {'fixtures': fixtures,
+                'totals': {'success': total_success, 'failure': total_failure,
+                           'error': total_error}
+               }
+        return 'bitten_summary_tests.html', data
--- a/bitten/report/tests/coverage.py
+++ b/bitten/report/tests/coverage.py
@@ -13,7 +13,6 @@
 
 from trac.db import DatabaseManager
 from trac.test import EnvironmentStub, Mock
-from trac.web.clearsilver import HDFWrapper
 from bitten.model import *
 from bitten.report import coverage
 from bitten.report.coverage import TestCoverageChartGenerator
@@ -39,15 +38,15 @@
         self.assertEqual(['coverage'], generator.get_supported_categories())
 
     def test_no_reports(self):
-        req = Mock(hdf=HDFWrapper())
+        req = Mock()
         config = Mock(name='trunk')
         generator = TestCoverageChartGenerator(self.env)
-        template = generator.generate_chart_data(req, config, 'coverage')
-        self.assertEqual('bitten_chart_coverage.cs', template)
-        self.assertEqual('Test Coverage', req.hdf['chart.title'])
-        self.assertEqual('', req.hdf['chart.data.0.0'])
-        self.assertEqual('Lines of code', req.hdf['chart.data.1.0'])
-        self.assertEqual('Coverage', req.hdf['chart.data.2.0'])
+        template, data = generator.generate_chart_data(req, config, 'coverage')
+        self.assertEqual('bitten_chart_coverage.html', template)
+        self.assertEqual('Test Coverage', data['title'])
+        self.assertEqual('', data['data'][0][0])
+        self.assertEqual('Lines of code', data['data'][1][0])
+        self.assertEqual('Coverage', data['data'][2][0])
 
     def test_single_platform(self):
         config = Mock(name='trunk')
@@ -59,17 +58,17 @@
         report.items += [{'lines': '12', 'percentage': '25'}]
         report.insert()
 
-        req = Mock(hdf=HDFWrapper())
+        req = Mock()
         generator = TestCoverageChartGenerator(self.env)
-        template = generator.generate_chart_data(req, config, 'coverage')
-        self.assertEqual('bitten_chart_coverage.cs', template)
-        self.assertEqual('Test Coverage', req.hdf['chart.title'])
-        self.assertEqual('', req.hdf['chart.data.0.0'])
-        self.assertEqual('[123]', req.hdf['chart.data.0.1'])
-        self.assertEqual('Lines of code', req.hdf['chart.data.1.0'])
-        self.assertEqual('12', req.hdf['chart.data.1.1'])
-        self.assertEqual('Coverage', req.hdf['chart.data.2.0'])
-        self.assertEqual('3', req.hdf['chart.data.2.1'])
+        template, data = generator.generate_chart_data(req, config, 'coverage')
+        self.assertEqual('bitten_chart_coverage.html', template)
+        self.assertEqual('Test Coverage', data['title'])
+        self.assertEqual('', data['data'][0][0])
+        self.assertEqual('[123]', data['data'][0][1])
+        self.assertEqual('Lines of code', data['data'][1][0])
+        self.assertEqual(12, data['data'][1][1])
+        self.assertEqual('Coverage', data['data'][2][0])
+        self.assertEqual(3, data['data'][2][1])
 
     def test_multi_platform(self):
         config = Mock(name='trunk')
@@ -88,17 +87,17 @@
         report.items += [{'lines': '12', 'percentage': '50'}]
         report.insert()
 
-        req = Mock(hdf=HDFWrapper())
+        req = Mock()
         generator = TestCoverageChartGenerator(self.env)
-        template = generator.generate_chart_data(req, config, 'coverage')
-        self.assertEqual('bitten_chart_coverage.cs', template)
-        self.assertEqual('Test Coverage', req.hdf['chart.title'])
-        self.assertEqual('', req.hdf['chart.data.0.0'])
-        self.assertEqual('[123]', req.hdf['chart.data.0.1'])
-        self.assertEqual('Lines of code', req.hdf['chart.data.1.0'])
-        self.assertEqual('12', req.hdf['chart.data.1.1'])
-        self.assertEqual('Coverage', req.hdf['chart.data.2.0'])
-        self.assertEqual('6', req.hdf['chart.data.2.1'])
+        template, data = generator.generate_chart_data(req, config, 'coverage')
+        self.assertEqual('bitten_chart_coverage.html', template)
+        self.assertEqual('Test Coverage', data['title'])
+        self.assertEqual('', data['data'][0][0])
+        self.assertEqual('[123]', data['data'][0][1])
+        self.assertEqual('Lines of code', data['data'][1][0])
+        self.assertEqual(12, data['data'][1][1])
+        self.assertEqual('Coverage', data['data'][2][0])
+        self.assertEqual(6, data['data'][2][1])
 
 
 def suite():
--- a/bitten/report/tests/testing.py
+++ b/bitten/report/tests/testing.py
@@ -12,7 +12,6 @@
 
 from trac.db import DatabaseManager
 from trac.test import EnvironmentStub, Mock
-from trac.web.clearsilver import HDFWrapper
 from bitten.model import *
 from bitten.report.testing import TestResultsChartGenerator
 
@@ -35,15 +34,15 @@
         self.assertEqual(['test'], generator.get_supported_categories())
 
     def test_no_reports(self):
-        req = Mock(hdf=HDFWrapper())
+        req = Mock()
         config = Mock(name='trunk')
         generator = TestResultsChartGenerator(self.env)
-        template = generator.generate_chart_data(req, config, 'test')
-        self.assertEqual('bitten_chart_tests.cs', template)
-        self.assertEqual('Unit Tests', req.hdf['chart.title'])
-        self.assertEqual('', req.hdf['chart.data.0.0'])
-        self.assertEqual('Total', req.hdf['chart.data.1.0'])
-        self.assertEqual('Failures', req.hdf['chart.data.2.0'])
+        template, data = generator.generate_chart_data(req, config, 'test')
+        self.assertEqual('bitten_chart_tests.html', template)
+        self.assertEqual('Unit Tests', data['title'])
+        self.assertEqual('', data['data'][0][0])
+        self.assertEqual('Total', data['data'][1][0])
+        self.assertEqual('Failures', data['data'][2][0])
 
     def test_single_platform(self):
         config = Mock(name='trunk')
@@ -55,17 +54,17 @@
                          {'status': 'success'}]
         report.insert()
 
-        req = Mock(hdf=HDFWrapper())
+        req = Mock()
         generator = TestResultsChartGenerator(self.env)
-        template = generator.generate_chart_data(req, config, 'test')
-        self.assertEqual('bitten_chart_tests.cs', template)
-        self.assertEqual('Unit Tests', req.hdf['chart.title'])
-        self.assertEqual('', req.hdf['chart.data.0.0'])
-        self.assertEqual('[123]', req.hdf['chart.data.0.1'])
-        self.assertEqual('Total', req.hdf['chart.data.1.0'])
-        self.assertEqual('3', req.hdf['chart.data.1.1'])
-        self.assertEqual('Failures', req.hdf['chart.data.2.0'])
-        self.assertEqual('1', req.hdf['chart.data.2.1'])
+        template, data = generator.generate_chart_data(req, config, 'test')
+        self.assertEqual('bitten_chart_tests.html', template)
+        self.assertEqual('Unit Tests', data['title'])
+        self.assertEqual('', data['data'][0][0])
+        self.assertEqual('[123]', data['data'][0][1])
+        self.assertEqual('Total', data['data'][1][0])
+        self.assertEqual(3, data['data'][1][1])
+        self.assertEqual('Failures', data['data'][2][0])
+        self.assertEqual(1, data['data'][2][1])
 
     def test_multi_platform(self):
         config = Mock(name='trunk')
@@ -86,17 +85,17 @@
                          {'status': 'failure'}]
         report.insert()
 
-        req = Mock(hdf=HDFWrapper())
+        req = Mock()
         generator = TestResultsChartGenerator(self.env)
-        template = generator.generate_chart_data(req, config, 'test')
-        self.assertEqual('bitten_chart_tests.cs', template)
-        self.assertEqual('Unit Tests', req.hdf['chart.title'])
-        self.assertEqual('', req.hdf['chart.data.0.0'])
-        self.assertEqual('[123]', req.hdf['chart.data.0.1'])
-        self.assertEqual('Total', req.hdf['chart.data.1.0'])
-        self.assertEqual('3', req.hdf['chart.data.1.1'])
-        self.assertEqual('Failures', req.hdf['chart.data.2.0'])
-        self.assertEqual('2', req.hdf['chart.data.2.1'])
+        template, data = generator.generate_chart_data(req, config, 'test')
+        self.assertEqual('bitten_chart_tests.html', template)
+        self.assertEqual('Unit Tests', data['title'])
+        self.assertEqual('', data['data'][0][0])
+        self.assertEqual('[123]', data['data'][0][1])
+        self.assertEqual('Total', data['data'][1][0])
+        self.assertEqual(3, data['data'][1][1])
+        self.assertEqual('Failures', data['data'][2][0])
+        self.assertEqual(2, data['data'][2][1])
 
 
 def suite():
deleted file mode 100644
--- a/bitten/templates/bitten_admin_configs.cs
+++ /dev/null
@@ -1,181 +0,0 @@
-<h2>Manage Build Configurations</h2><?cs
-
-if admin.config.name ?>
- <form class="mod" id="modconfig" method="post">
-  <table class="form" summary=""><tr>
-   <td class="name"><label>Name:<br />
-    <input type="text" name="name" value="<?cs var:admin.config.name ?>" />
-   </label></td>
-   <td class="label"><label>Label (for display):<br />
-    <input type="text" name="label" size="32" value="<?cs
-      var:admin.config.label ?>" />
-   </label></td>
-  </tr><tr>
-   <td colspan="2"><fieldset class="iefix">
-    <label for="description">Description (you may use <a tabindex="42" href="<?cs
-      var:trac.href.wiki ?>/WikiFormatting">WikiFormatting</a> here):</label>
-    <p><textarea id="description" name="description" class="wikitext" rows="3" cols="65"><?cs
-      var:admin.config.description ?></textarea></p>
-    <script type="text/javascript" src="<?cs
-      var:chrome.href ?>/common/js/wikitoolbar.js"></script>
-   </fieldset></td>
-  </tr><tr>
-   <td colspan="2"><fieldset class="iefix">
-    <label for="recipe">Recipe:</label>
-    <p><textarea id="recipe" name="recipe" rows="8" cols="78"><?cs
-     var:admin.config.recipe ?></textarea></p>
-   </fieldset></td>
-  </tr></table>
-  <fieldset id="repos">
-   <legend>Repository Mapping</legend>
-   <table class="form" summary=""><tr>
-    <th><label for="path">Path:</label></th>
-    <td colspan="3"><input type="text" name="path" size="48" value="<?cs
-      var:admin.config.path ?>" /></td>
-   </tr><tr>
-    <th><label for="min_rev">Oldest revision:</label></th>
-    <td><input type="text" name="min_rev" size="8" value="<?cs
-      var:admin.config.min_rev ?>" /></td>
-    <th><label for="min_rev">Youngest revision:</label></th>
-    <td><input type="text" name="max_rev" size="8" value="<?cs
-      var:admin.config.max_rev ?>" /></td>
-   </table>
-  </fieldset>
-  <div class="buttons">
-   <input type="submit" name="cancel" value="Cancel" />
-   <input type="submit" name="save" value="Save" />
-  </div>
-  <div class="platforms">
-   <h3>Target Platforms</h3>
-   <table class="listing" id="platformlist">
-    <thead>
-     <tr><th class="sel">&nbsp;</th><th>Name</th><th>Rules</th></tr>
-    </thead><?cs each:platform = admin.config.platforms ?><tr>
-      <td class="sel"><input type="checkbox" name="sel" value="<?cs
-        var:platform.id ?>" /></td>
-      <td class="name"><a href="<?cs var:platform.href?>"><?cs
-        var:platform.name ?></a></td>
-      <td class="rules"><?cs if:len(platform.rules) ?><ul><?cs
-       each:rule = platform.rules ?><li><code>
-        <strong><?cs var:rule.property ?></strong> ~= <?cs var:rule.pattern ?>
-       </code></li><?cs
-       /each ?></ul><?cs
-      /if ?></td>
-     </tr><?cs
-    /each ?>
-   </table>
-   <div class="buttons">
-    <input type="submit" name="new" value="Add platform" />
-    <input type="submit" name="remove" value="Delete selected platforms" />
-   </div>
-  </div>
- </form><?cs
-
-elif len(admin.platform) ?>
- <form class="mod" id="modplatform" method="post">
-  <div class="field"><label>Target Platform:
-   <input type="text" name="name" value="<?cs var:admin.platform.name ?>" />
-  </label></div>
-  <fieldset>
-   <legend>Rules</legend>
-   <table><thead><tr>
-    <th>Property name</th><th>Match pattern</th>
-   </tr></thead><tbody><?cs
-    each:rule = admin.platform.rules ?><tr>
-     <td><input type="text" name="property_<?cs var:name(rule) ?>" value="<?cs
-      var:rule.property ?>" /></td>
-     <td><input type="text" name="pattern_<?cs var:name(rule) ?>" value="<?cs
-      var:rule.pattern ?>" /></td>
-     <td><input type="submit" name="add_rule_<?cs
-       var:name(rule) ?>" value="+" /><input type="submit" name="rm_rule_<?cs
-       var:name(rule) ?>" value="-" />
-     </td>
-    </tr><?cs /each ?>
-   </tbody></table>
-  </fieldset>
-  <p class="help">
-   The property name can be any of a set of standard default properties, or
-   custom properties defined in slave configuration files. The default
-   properties are:
-  </p>
-  <dl class="help">
-   <dt><code>os<code>:</dt>
-   <dd>The name of the operating system (for example "Darwin")</dd>
-   <dt><code>family<code>:</dt>
-   <dd>The type of operating system (for example "posix" or "nt")</dd>
-   <dt><code>version<code>:</dt>
-   <dd>The operating system version (for example "8.10.1)</dd>
-   <dt><code>machine<code>:</dt>
-   <dd>The hardware architecture (for example "i386")</dd>
-   <dt><code>processor<code>:</dt>
-   <dd>The CPU model (for example "i386", this may be empty or the same as for
-     <code>machine</code>)</dd>
-   <dt><code>name<code>:</dt>
-   <dd>The name of the slave</dd>
-   <dt><code>ipnr<code>:</dt>
-   <dd>The IP address of the slave</dd>
-  </dl>
-  <p class="help">The match pattern is a regular expression.</p>
-  <div class="buttons">
-   <form method="get" action=""><div>
-    <input type="hidden" name="<?cs
-     if:admin.platform.exists ?>edit<?cs else ?>new<?cs /if ?>" value="" />
-    <input type="hidden" name="platform" value="<?cs
-     var:admin.platform.id ?>" />
-    <input type="submit" name="cancel" value="Cancel" />
-    <?cs if:admin.platform.exists ?>
-     <input type="submit" name="save" value="Save" />
-    <?cs else ?>
-     <input type="submit" name="add" value="Add" />
-    <?cs /if ?>
-   </div></form>
-  </div>
- </form><?cs
-
-else ?>
- <form class="addnew" id="addcomp" method="post">
-  <fieldset>
-   <legend>Add Configuration:</legend>
-   <table summary=""><tr>
-    <td class="name"><div class="field"><label>Name:<br />
-     <input type="text" name="name" size="12" />
-    </label></div></td>
-    <td class="label"><div class="field"><label>Label:<br />
-     <input type="text" name="label" size="22" />
-    </label></div></td>
-   </tr><tr>
-     <td class="path" colspan="2"><div class="field">
-      <label>Path:<br /><input type="text" name="path" size="32" /></label>
-     </div>
-   </tr></table>
-   <div class="buttons">
-    <input type="submit" name="add" value="Add">
-   </div>
-  </fieldset>
- </form>
-
- <form method="POST">
-  <table class="listing" id="configlist">
-   <thead>
-    <tr><th class="sel">&nbsp;</th><th>Name</th>
-    <th>Path</th><th>Active</th></tr>
-   </thead><?cs each:config = admin.configs ?>
-    <tr>
-     <td class="sel"><input type="checkbox" name="sel" value="<?cs
-       var:config.name ?>" /></td>
-     <td class="name"><a href="<?cs var:config.href?>"><?cs
-       var:config.label ?></a></td>
-     <td class="path"><code><?cs var:config.path ?></code></td>
-     <td class="active"><input type="checkbox" name="active" value="<?cs
-       var:config.name ?>"<?cs
-       if:config.active ?> checked="checked" <?cs /if ?>></td>
-    </tr><?cs
-   /each ?>
-  </table>
-  <div class="buttons">
-   <input type="submit" name="remove" value="Remove selected items" />
-   <input type="submit" name="apply" value="Apply changes" />
-  </div>
- </form><?cs
-
-/if ?>
new file mode 100755
--- /dev/null
+++ b/bitten/templates/bitten_admin_configs.html
@@ -0,0 +1,240 @@
+<!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:xi="http://www.w3.org/2001/XInclude"
+      xmlns:py="http://genshi.edgewall.org/">
+  <xi:include href="admin.html" />
+  <head>
+    <title>Manage Build Configurations</title>
+  </head>
+  <body>
+    <h2>Manage Build Configurations</h2>
+    <py:choose><form py:when="config" class="mod"
+         id="modconfig" method="post" action="">
+      <table class="form" summary=""><tr>
+        <td class="name"><label>Name:<br />
+          <input type="text" name="name"
+                 value="$config.name" />
+        </label></td>
+        <td class="label"><label>Label (for display):<br />
+          <input type="text" name="label" size="32"
+                 value="$config.label" />
+        </label></td>
+      </tr><tr>
+        <td colspan="2"><fieldset class="iefix">
+          <label for="description">
+            Description (you may use 
+            <a tabindex="42"
+               href="${href.wiki('WikiFormatting')}">
+              WikiFormatting</a> here):
+          </label>
+          <p>
+            <textarea id="description" name="description"
+                      class="wikitext" rows="3" cols="65">$config.description</textarea>
+          </p>
+          <script type="text/javascript" src="${chrome.htdocs_location}js/wikitoolbar.js"></script>
+        </fieldset></td>
+      </tr><tr>
+        <td colspan="2"><fieldset class="iefix">
+          <label for="recipe">Recipe:</label>
+          <p>
+            <textarea id="recipe" name="recipe" rows="8" cols="78">$config.recipe</textarea>
+          </p>
+        </fieldset></td>
+      </tr></table>
+      <fieldset id="repos">
+        <legend>Repository Mapping</legend>
+        <table class="form" summary=""><tr>
+          <th><label for="path">Path:</label></th>
+          <td colspan="3"><input id="path" type="text" name="path"
+              size="48" value="$config.path" /></td>
+        </tr><tr>
+          <th><label for="min_rev">
+            Oldest revision:
+          </label></th>
+          <td><input id="min_rev" type="text" name="min_rev" size="8"
+                     value="$config.min_rev" /></td>
+          <th><label for="max_rev">
+            Youngest revision:
+          </label></th>
+          <td><input id="max_rev" type="text" name="max_rev" size="8"
+                     value="$config.max_rev" /></td>
+        </tr></table>
+      </fieldset>
+      <div class="buttons">
+        <input type="submit" name="cancel"
+               value="Cancel" />
+        <input type="submit" name="save" value="Save" />
+      </div>
+      <div class="platforms">
+        <h3>Target Platforms</h3>
+        <table class="listing" id="platformlist">
+          <thead>
+            <tr><th class="sel">&nbsp;</th>
+            <th>Name</th><th>Rules</th></tr>
+          </thead>
+          <tbody>
+            <tr py:if="not config.platforms">
+              <td colspan="3"><em>(No Platforms)</em></td>
+            </tr>
+            <tr py:for="platform in config.platforms">
+              <td class="sel">
+                <input type="checkbox" name="sel"
+                       value="$platform.id" />
+              </td>
+              <td class="name"><a href="$platform.href">
+                $platform.name
+              </a></td>
+              <td class="rules">
+                <ul py:if="len(platform.rules)">
+                  <li py:for="rule in platform.rules">
+                    <code>
+                      <strong>$rule.property</strong> ~= 
+                      $rule.pattern
+                    </code>
+                  </li>
+                </ul>
+              </td>
+            </tr>
+          </tbody>
+        </table>
+        <div class="buttons">
+          <input type="submit" name="new"
+                 value="Add platform" />
+          <input type="submit" name="remove"
+                 value="Delete selected platforms" />
+        </div>
+      </div>
+    </form>
+
+    <form py:when="platform" class="mod" id="modplatform"
+          method="post" action="">
+      <div class="field"><label>Target Platform:
+        <input type="text" name="name"
+               value="$platform.name" />
+      </label></div>
+      <fieldset>
+        <legend>Rules</legend>
+        <table><thead><tr>
+          <th>Property name</th><th>Match pattern</th>
+        </tr></thead><tbody>
+          <tr py:for="idx, rule in enumerate(platform.rules)">
+            <td><input type="text" name="property_${idx}"
+                       value="$rule.property" /></td>
+            <td><input type="text" name="pattern_${idx}" 
+                       value="$rule.pattern" /></td>
+            <td><input type="submit"
+                       name="add_rule_${idx}" value="+" />
+                <input type="submit" name="rm_rule_${idx}"
+                       value="-" />
+            </td>
+          </tr>
+        </tbody></table>
+      </fieldset>
+      <p class="help">
+        The property name can be any of a set of standard
+        default properties, or custom properties defined
+        in slave configuration files. The default 
+        properties are:
+      </p>
+      <dl class="help">
+        <dt><code>os</code>:</dt>
+        <dd>The name of the operating system (for example
+            "Darwin")</dd>
+        <dt><code>family</code>:</dt>
+        <dd>The type of operating system (for example
+            "posix" or "nt")</dd>
+        <dt><code>version</code>:</dt>
+        <dd>The operating system version (for example
+            "8.10.1)</dd>
+        <dt><code>machine</code>:</dt>
+        <dd>The hardware architecture (for example "i386"</dd>
+        <dt><code>processor</code>:</dt>
+        <dd>The CPU model (for example "i386", this may be
+            empty or the same as for <code>machine</code>
+        </dd>
+        <dt><code>name</code>:</dt>
+        <dd>The name of the slave</dd>
+        <dt><code>ipnr</code>:</dt>
+        <dd>The IP address of the slave</dd>
+      </dl>
+      <p class="help">
+        The match pattern is a regular expression.
+      </p>
+      <div class="buttons">
+        <form method="get" action=""><div>
+          <input type="hidden"
+                 name="${platform.exists and 'edit'
+                         or 'new'}" value="" />
+          <input type="hidden" name="platform"
+                 value="$platform.id" />
+          <input type="submit" name="cancel"
+                 value="Cancel" /><py:choose>
+          <input py:when="platform.exists" type="submit"
+                 name="save" value="Save" />
+          <input py:otherwise="" type="submit"
+                 name="add" value="Add" /></py:choose>
+        </div></form>
+      </div>
+    </form>
+
+    <py:otherwise><form class="addnew" id="addcomp"
+                        method="post" action=""><fieldset>
+      <legend>Add Configuration:</legend>
+      <table summary=""><tr>
+        <td class="name"><div  
+            class="field"><label>Name:<br />
+          <input type="text" name="name" size="12" />
+        </label></div></td>
+        <td class="label"><div
+            class="field"><label>Label:<br />
+          <input type="text" name="label" size="22" />
+        </label></div></td>
+      </tr><tr>
+        <td class="path" colspan="2"><div class="field">
+          <label>Path:<br />
+            <input type="text" name="path" size="32" />
+          </label>
+        </div></td>
+      </tr></table>
+      <div class="buttons">
+        <input type="submit" name="add" value="Add" />
+      </div>
+    </fieldset></form>
+
+    <form method="post" action="">
+      <table class="listing" id="configlist">
+        <thead>
+          <tr><th class="sel">&nbsp;</th><th>Name</th>
+          <th>Path</th><th>Active</th></tr>
+        </thead><tbody>
+        <tr py:if="not configs">
+          <td colspan="4"><em>(No Build Configurations)</em></td>
+        </tr>
+        <tr py:for="config in configs">
+          <td class="sel">
+            <input type="checkbox" name="sel"
+                   value="$config.name" />
+          </td>
+          <td class="name">
+            <a href="$config.href">$config.label</a>
+          </td>
+          <td class="path"><code>$config.path</code></td>
+          <td class="active">
+            <input type="checkbox" name="active"
+                   value="$config.name"
+                   checked="${config.active and 'checked'
+                              or None}" />
+          </td>
+        </tr>
+      </tbody></table>
+      <div class="buttons">
+        <input type="submit" name="remove"
+               value="Remove selected items" />
+        <input type="submit" name="apply"
+               value="Apply changes" />
+      </div>
+    </form></py:otherwise></py:choose>
+  </body>
+</html>
deleted file mode 100644
--- a/bitten/templates/bitten_admin_master.cs
+++ /dev/null
@@ -1,59 +0,0 @@
-<h2>Manage Build Master</h2>
-
-<form class="mod" id="bitten" method="post">
-
-  <fieldset id="config">
-    <legend>Configuration Options</legend>
-    <div class="field">
-      <label>
-        <input type="checkbox" id="build_all" name="build_all"
-               <?cs if:admin.master.build_all ?> checked="checked"<?cs /if ?> />
-        Build all revisions
-      </label>
-    </div>
-    <p class="hint">
-      Whether to build older revisions even when a more recent revision has
-      already been built.
-    </p>
-    <div class="field">
-      <label>
-        <input type="checkbox" id="adjust_timestamps" name="adjust_timestamps"
-               <?cs if:admin.master.adjust_timestamps ?> checked="checked"<?cs /if ?> />
-        Adjust build timestamps
-      </label>
-    </div>
-    <p class="hint">
-      Whether the timestamps of builds should be adjusted to be close to the
-      timestamps of the corresponding changesets.
-    </p>
-    <hr />
-    <div class="field">
-      <label>
-        Time to wait for stabilization:
-        <input type="text" id="stabilize_wait" name="stabilize_wait"
-               value="<?cs var:admin.master.stabilize_wait ?>" size="5" />
-      </label>
-    </div>
-    <p class="hint">
-      The time in seconds to wait for the repository to stabilize after a
-      check-in before initiating a build.
-    </p>
-    <hr />
-    <div class="field">
-      <label>
-        Connection timeout for build slaves:
-        <input type="text" id="slave_timeout" name="slave_timeout"
-               value="<?cs var:admin.master.slave_timeout ?>" size="5" />
-      </label>
-    </div>
-    <p class="hint">
-      The timeout in seconds after which a build started by a slave is
-      considered aborted, in case there has been no activity from that slave
-      in that time.
-    </p>
-  </fieldset>
-
-  <div class="buttons">
-    <input type="submit" value="Apply changes"/>
-  </div>
-</form>
new file mode 100755
--- /dev/null
+++ b/bitten/templates/bitten_admin_master.html
@@ -0,0 +1,73 @@
+<!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:xi="http://www.w3.org/2001/XInclude"
+      xmlns:py="http://genshi.edgewall.org/">
+  <xi:include href="admin.html" />
+  <head>
+    <title>Manage Build Master</title>
+  </head>
+  <body>
+    <h2>Manage Build Master</h2>
+
+    <form class="mod" id="bitten" method="post" action="">
+      <fieldset id="config">
+        <legend>Configuration Options</legend>
+        <div class="field">
+          <label>
+            <input type="checkbox" id="build_all" name="build_all"
+                   checked="${master.build_all and 'checked' or None}" />
+            Build all revisions
+          </label>
+        </div>
+        <p class="hint">
+          Whether to build older revisions even when a more recent
+          revision has already been built.
+        </p>
+        <div class="field">
+          <label>
+            <input type="checkbox" id="adjust_timestamps"
+                   name="adjust_timestamps"
+                   checked="${master.adjust_timestamps and 'checked'
+                              or None}" />
+            Adjust build timestamps
+          </label>
+        </div>
+        <p class="hint">
+          Whether the timestamps of builds should be adjusted to be
+          close to the timestamps of the corresponding changesets.
+        </p>
+        <hr />
+        <div class="field">
+          <label>
+            Time to wait for stabilization:
+            <input type="text" id="stabilize_wait" name="stabilize_wait"
+                   value="$master.stabilize_wait" size="5" />
+          </label>
+        </div>
+        <p class="hint">
+          The time in seconds to wait for the repository to stabilize
+          after a check-in before initiating a build.
+        </p>
+        <hr />
+        <div class="field">
+          <label>
+            Connection timeout for build slaves:
+            <input type="text" id="slave_timeout" name="slave_timeout"
+                   value="$master.slave_timeout" size="5" />
+          </label>
+        </div>
+        <p class="hint">
+          The timeout in seconds after which a build started by a slave
+          is considered aborted, in case there has been no activity from
+          that slave in that time.
+        </p>
+      </fieldset>
+
+      <div class="buttons">
+        <input type="submit" value="Apply changes"/>
+      </div>
+    </form>
+  </body>
+</html>
deleted file mode 100644
--- a/bitten/templates/bitten_build.cs
+++ /dev/null
@@ -1,84 +0,0 @@
-<?cs include:"header.cs" ?>
- <script type="text/javascript" src="<?cs
-   var:chrome.href ?>/bitten/tabset.js"></script>
- <div id="ctxtnav" class="nav"></div>
- <div id="content" class="build">
-  <h1><?cs var:title ?></h1>
-  <dl id="overview">
-   <dt class="config">Configuration:</dt>
-   <dd class="config"><a href="<?cs var:build.config.href ?>"><?cs
-    var:build.config.name ?></a>
-   </dd>
-   <dt class="trigger">Triggered by:</dt>
-   <dd class="trigger">Changeset <a href="<?cs
-    var:build.chgset_href ?>">[<?cs var:build.rev ?>]</a> by <?cs
-    var:build.chgset_author ?>
-   </dd>
-   <dt class="slave">Built by:</dt>
-   <dd class="slave"><code><?cs var:build.slave.name ?></code> (<?cs
-    var:build.slave.ipnr ?>)
-   </dd>
-   <dt class="os">Operating system:</dt>
-   <dd><?cs var:build.slave.os.name ?> <?cs var:build.slave.os.version ?> (<?cs
-    var:build.slave.os.family ?>)
-   </dd><?cs
-   if:build.slave.machine ?>
-    <dt class="machine">Hardware:</dt>
-    <dd class="machine"><?cs
-     var:build.slave.machine ?><?cs
-     if:build.slave.processor ?> (<?cs
-      var:build.slave.processor ?>)<?cs
-     /if ?>
-    </dd><?cs
-   /if ?>
-   <dt class="time"><?cs if:build.stopped ?>Started:<?cs else ?>Building since:<?cs /if ?>
-   <dd class="time"><?cs var:build.started ?> (<?cs
-    var:build.started_delta ?> ago)
-   </dd>
-   <?cs if:build.stopped ?>
-     <dt class="time">Stopped:</dt>
-     <dd class="time"><?cs var:build.stopped ?> (<?cs var:build.stopped_delta ?> ago) </dd>
-   <?cs /if ?>
-   <dt class="duration">Duration:</dt>
-   <dd class="duration"><?cs
-    var:build.duration ?>
-   </dd>
-  </dl><?cs
-  if:build.can_delete ?>
-   <div class="buttons">
-    <form method="post" action=""><div>
-     <input type="hidden" name="action" value="invalidate" />
-     <input type="submit" value="Invalidate build" />
-    </div></form>
-   </div><?cs
-  /if ?><?cs
-  each:step = build.steps ?>
-   <h2 class="step" id="step_<?cs var:step.name ?>"><?cs var:step.name ?> (<?cs
-     var:step.duration ?>)</h2><?cs
-   if:len(step.errors) ?>
-    <div class="errors">
-     <h3>Errors</h3><ul><?cs
-     each:error = step.errors ?><li><?cs var:error ?></li><?cs
-     /each ?></ul>
-    </div><?cs
-   /if ?><p><?cs var:step.description ?></p>
-   <div id="<?cs var:step.name ?>_tabs">
-    <div class="tab"><h3>Log</h3><div class="log"><?cs
-     each:item = step.log ?><code class="<?cs var:item.level ?>"><?cs
-      var:item.message ?></code><br /><?cs
-     /each ?></div>
-    </div><?cs
-    each:report = step.reports ?><?cs
-     if:report.summary ?>
-      <div class="tab report <?cs var:report.type ?>">
-       <?cs var:report.summary ?>
-      </div><?cs
-     /if ?><?cs
-    /each ?>
-   </div>
-   <script type="text/javascript">
-     makeTabSet(document.getElementById("<?cs var:step.name ?>_tabs"));
-   </script><?cs
-  /each ?>
- </div>
-<?cs include:"footer.cs" ?>
new file mode 100755
--- /dev/null
+++ b/bitten/templates/bitten_build.html
@@ -0,0 +1,73 @@
+<!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:xi="http://www.w3.org/2001/XInclude"
+      xmlns:py="http://genshi.edgewall.org/">
+  <xi:include href="layout.html" />
+  <head>
+    <title>$title</title>
+  </head>
+  <body>
+    <div id="content" class="build">
+      <h1>$title</h1>
+      <dl id="overview" py:with="slave = build.slave">
+        <dt class="config">Configuration:</dt>
+        <dd class="config">
+          <a href="$build.config.href">$build.config.name</a>
+        </dd>
+        <dt class="trigger">Triggered by:</dt>
+        <dd class="trigger">
+          Changeset <a href="$build.chgset_href">[$build.rev]</a> by 
+          $build.chgset_author
+        </dd>
+        <dt class="slave">Built by:</dt>
+        <dd class="slave">
+          <code>$slave.name</code> ($slave.ipnr)
+        </dd>
+        <dt class="os">Operating system:</dt>
+        <dd>$slave.os_name $slave.os_version ($slave.os_family)</dd>
+        <py:if test="slave.machine"><dt class="machine">Hardware:</dt>
+        <dd class="machine">
+          $slave.machine
+          <py:if test="slave.processor"> ($slave.processor)</py:if>
+        </dd></py:if>
+        <dt class="time">
+          ${build.stopped and 'Started:' or 'Building since:'}
+        </dt>
+        <dd class="time">$build.started ($build.started_delta ago)</dd>
+        <py:if test="build.stopped"><dt class="time">Stopped:</dt>
+        <dd class="time">$build.stopped ($build.stopped_delta ago)</dd></py:if>
+        <dt class="duration">Duration:</dt>
+        <dd class="duration">$build.duration</dd>
+      </dl>
+      <div py:if="build.can_delete" class="buttons">
+        <form method="post" action=""><div>
+          <input type="hidden" name="action" value="invalidate" />
+          <input type="submit" value="Invalidate build" />
+        </div></form>
+      </div><py:for each="step in build.steps">
+      <h2 class="step" id="step_${step.name}">$step.name ($step.duration)</h2>
+      <div py:if="step.errors" class="errors">
+        <h3>Errors</h3>
+        <ul>
+          <li py:for="error in step.errors">$error</li>
+        </ul>
+      </div>
+      <p>$step.description</p>
+      <div id="${step.name}_tabs">
+        <div class="tab">
+          <h3>Log</h3>
+          <div class="log"><py:for each="item in step.log"><code class="$item.level">$item.message</code><br /></py:for></div>
+        </div>
+        <div py:for="report in [r for r in step.reports if r.template]"
+             class="tab report $report.category">
+          <xi:include href="$report.template" py:with="data = report.data" />
+        </div>
+      </div>
+      <script type="text/javascript">
+        makeTabSet(document.getElementById("${step.name}_tabs"));
+      </script></py:for>
+    </div>
+  </body>
+</html>
deleted file mode 100644
--- a/bitten/templates/bitten_chart_coverage.cs
+++ /dev/null
@@ -1,40 +0,0 @@
-<chart>
- <chart_type>
-  <value>area</value>
-  <value>area</value>
- </chart_type>
-
- <axis_category size="10" orientation="diagonal_up"
-   skip="<?cs var:len(chart.data.0) / 6 ?>"/>
- <axis_ticks value_ticks="false" category_ticks="true" major_thickness="1"
-   minor_thickness="0" major_color="000000" position="outside"/>
-
- <chart_data><?cs
-  each:row = chart.data ?><row><?cs
-   each:value = row ?><?cs
-    if:name(row) == 0 || name(value) == 0 ?><string><?cs
-     var:value ?></string><?cs
-    else ?><number><?cs
-     var:value ?></number><?cs
-    /if ?><?cs
-   /each ?></row><?cs
-  /each ?></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>
-  <color>bbbbbb</color>
-  <color>9999ff</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" ><?cs
-    var:chart.title ?></text>
- </draw>
-
-</chart>
new file mode 100755
--- /dev/null
+++ b/bitten/templates/bitten_chart_coverage.html
@@ -0,0 +1,38 @@
+<chart xmlns:py="http://genshi.edgewall.org/">
+ <chart_type>
+  <value>area</value>
+  <value>area</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>
+  <color>bbbbbb</color>
+  <color>9999ff</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>
deleted file mode 100644
--- a/bitten/templates/bitten_chart_tests.cs
+++ /dev/null
@@ -1,40 +0,0 @@
-<chart>
- <chart_type>
-  <value>area</value>
-  <value>column</value>
- </chart_type>
-
- <axis_category size="10" orientation="diagonal_up"
-   skip="<?cs var:len(chart.data.0) / 6 ?>"/>
- <axis_ticks value_ticks="false" category_ticks="true" major_thickness="2"
-   minor_thickness="0" major_color="000000" position="outside"/>
-
- <chart_data><?cs
-  each:row = chart.data ?><row><?cs
-   each:value = row ?><?cs
-    if:name(row) == 0 || name(value) == 0 ?><string><?cs
-     var:value ?></string><?cs
-    else ?><number><?cs
-     var:value ?></number><?cs
-    /if ?><?cs
-   /each ?></row><?cs
-  /each ?></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>
-  <color>99dd99</color>
-  <color>ff0000</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" ><?cs
-    var:chart.title ?></text>
- </draw>
-
-</chart>
new file mode 100755
--- /dev/null
+++ b/bitten/templates/bitten_chart_tests.html
@@ -0,0 +1,38 @@
+<chart xmlns:py="http://genshi.edgewall.org/">
+ <chart_type>
+  <value>area</value>
+  <value>column</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="2"
+   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>
+  <color>99dd99</color>
+  <color>ff0000</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>
deleted file mode 100644
--- a/bitten/templates/bitten_config.cs
+++ /dev/null
@@ -1,365 +0,0 @@
-<?cs include:"header.cs" ?>
- <div id="ctxtnav" class="nav"><?cs
- if:page.mode == 'view_config' ?><?cs
-  with:links = chrome.links ?><?cs
-   if:len(links.prev) || len(links.next) ?><ul><?cs
-    if:len(links.prev) ?>
-     <li class="first<?cs if:!len(links.up) && !len(links.next) ?> last<?cs /if ?>">
-      &larr; <a href="<?cs var:links.prev.0.href ?>"><?cs
-        var:links.prev.0.title ?></a>
-     </li><?cs
-    /if ?><?cs
-    if:len(links.next) ?>
-     <li class="<?cs if:!len(links.prev) && !len(links.up) ?>first <?cs /if ?>last">
-      <a href="<?cs var:links.next.0.href ?>"><?cs
-        var:links.next.0.title ?></a> &rarr;
-     </li><?cs
-    /if ?></ul><?cs
-   /if ?><?cs
-  /with ?><?cs
- elif:page.mode == 'overview' ?><?cs
-  with:links = chrome.links ?><?cs
-    if:len(links.views) ?><ul>
-     <li class="last">
-      <a href="<?cs var:links.views.0.href ?>"><?cs
-        var:links.views.0.title ?></a>
-     </li><?cs
-    /if ?></ul><?cs
-  /with ?><?cs
- /if ?></div>
- <div id="content" class="build">
-  <h1><?cs var:title ?></h1><?cs
-
-  if:page.mode == 'overview' ?>
-   <form id="prefs" method="get">
-    <div>
-     <input type="checkbox" id="showall" name="show" value="all"<?cs
-      if:config.show_all ?> checked="checked"<?cs /if ?> />
-     <label for="showall">Show deactivated configurations</label>
-    </div>
-    <div class="buttons">
-     <input type="submit" value="Update" />
-    </div>
-   </form><?cs
-   each:config = configs ?>
-    <h2 class="config <?cs
-     if:!config.active ?>deactivated<?cs
-     /if ?>"><a href="<?cs var:config.href ?>"><?cs
-     var:config.label ?></a></h2><?cs
-    if:config.description ?><div class="description"><?cs
-     var:config.description ?></div><?cs
-    /if ?><?cs
-    if:len(config.builds) ?>
-     <h3 class="builds"><a href="<?cs var:config.href ?>">Latest builds</a></h3>
-     <table class="builds"><tr>
-      <th><a href="<?cs var:config.youngest_rev.href ?>">[<?cs
-       var:config.youngest_rev.id ?>]</a> by <?cs
-       var:config.youngest_rev.author ?><p class="date"><?cs
-       var:config.youngest_rev.date ?></p><p class="message"><?cs
-       var:config.youngest_rev.message ?></p>
-      </th><?cs
-      each:build = config.builds ?><td class="<?cs
-       var:build.cls ?>"><?cs
-        if:build.status != 'pending' ?><a href="<?cs var:build.href ?>"><?cs
-         var:build.platform ?></a><p class="date"><?cs
-         var:build.stopped ?></p><div class="system">
-         <strong><?cs var:build.slave.name ?></strong> (<?cs
-         var:build.slave.ipnr ?>)<br /><?cs
-         var:build.slave.os.name ?> <?cs var:build.slave.os.version ?><?cs
-         if:build.slave.machine || build.slave.processor ?> / <?cs
-          alt:build.slave.processor ?><?cs
-           var:build.slave.machine ?><?cs
-          /alt ?><?cs
-         /if ?></div><strong class="status"><?cs
-         if:build.status == 'completed' ?>Success<?cs
-         elif:build.status == 'failed' ?>Failed<?cs
-         elif:build.status == 'in progress' ?>In-progress<?cs
-         /if ?></strong><?cs
-        else ?><strong><?cs var:build.platform ?></strong>
-         <p class="nobuild">No build yet</p><?cs
-        /if ?>
-       </td><?cs
-      /each ?>
-     </tr></table><?cs
-    /if ?><?cs
-   /each ?><?cs
-   if:config.can_create ?><div class="buttons">
-    <form method="get" action=""><div>
-     <input type="hidden" name="action" value="new" />
-     <input type="submit" value="Add configuration" />
-    </div></form></div><?cs
-   /if ?></div><?cs
-
-  elif:page.mode == 'edit_config' ?>
-   <form class="config" method="post" action="">
-    <table summary=""><tr>
-     <td class="name"><label>Name:<br />
-      <input type="text" name="name" value="<?cs var:config.name ?>" />
-     </label></td>
-     <td class="label"><label>Label (for display):<br />
-      <input type="text" name="label" size="32" value="<?cs
-        var:config.label ?>" />
-     </label></td>
-    </tr><tr>
-     <td colspan="2"><fieldset class="iefix">
-      <label for="description">Description (you may use <a tabindex="42" href="<?cs
-        var:trac.href.wiki ?>/WikiFormatting">WikiFormatting</a> here):</label>
-      <p><textarea id="description" name="description" class="wikitext" rows="5" cols="78"><?cs
-        var:config.description ?></textarea></p>
-      <script type="text/javascript" src="<?cs
-        var:htdocs_location ?>js/wikitoolbar.js"></script>
-     </fieldset></td>
-    </tr></table>
-    <fieldset id="recipe">
-     <legend>Build Recipe</legend>
-     <textarea id="recipe" name="recipe" rows="8" cols="78"><?cs
-       var:config.recipe ?></textarea>
-    </fieldset>
-    <fieldset id="repos">
-     <legend>Repository Mapping</legend>
-     <table summary=""><tr>
-      <th><label for="path">Path:</label></th>
-      <td colspan="3"><input type="text" name="path" size="48" value="<?cs
-        var:config.path ?>" /></td>
-     </tr><tr>
-      <th><label for="min_rev">Oldest revision:</label></th>
-      <td><input type="text" name="min_rev" size="8" value="<?cs
-        var:config.min_rev ?>" /></td>
-      <th><label for="min_rev">Youngest revision:</label></th>
-      <td><input type="text" name="max_rev" size="8" value="<?cs
-        var:config.max_rev ?>" /></td>
-     </table>
-    </fieldset>
-    <div class="buttons">
-     <input type="hidden" name="action" value="<?cs
-       if:config.exists ?>edit<?cs else ?>new<?cs /if ?>" />
-     <input type="submit" value="<?cs
-       if:config.exists ?>Save changes<?cs else ?>Create<?cs /if ?>" />
-     <input type="submit" name="cancel" value="Cancel" />
-    </div>
-   </form><?cs
-   if:config.exists ?><div class="platforms">
-     <form class="platforms" method="post" action="">
-      <h2>Target Platforms</h2><?cs
-       if:len(config.platforms) ?><ul><?cs
-        each:platform = config.platforms ?>
-         <li><input type="checkbox" name="delete_platform" value="<?cs
-          var:platform.id ?>"> <a href="<?cs
-          var:platform.href ?>"><?cs var:platform.name ?></a>
-         </li><?cs
-        /each ?></ul><?cs
-       /if ?>
-      <div class="buttons">
-       <input type="submit" name="new" value="Add target platform" />
-       <input type="submit" name="delete" value="Delete selected platforms" />
-      </div>
-     </form>
-    </div><?cs
-   /if ?><?cs
-
- elif:page.mode == 'delete_config' ?>
-  <p><strong>Are you sure you want to delete the build configuration "<?cs
-    var:config.name ?>?</strong></p>
-  <p>This will also delete all builds performed for that configuration.</p>
-  <form action="" method="POST"><div class="buttons">
-   <input type="hidden" name="action" value="delete" />
-   <input type="submit" name="cancel" value="Cancel" />
-   <input type="submit" value="Delete configuration" />
-  </div></form><?cs
-
-  elif:page.mode == 'view_config' ?><?cs
-   if:config.can_modify ?><form id="prefs" method="post" class="activation"><?cs
-    if:!config.active ?><div class="help">This build configuration is currently
-     inactive.<br /> No builds will be initiated for this configuration<br />
-     until it is activated.</div><?cs
-    else ?><div class="help">This configuration is currently active.</div><?cs
-    /if ?>
-    <div class="buttons">
-     <input type="hidden" name="action" value="edit" /><?cs
-     if:config.active ?>
-      <input type="submit" name="deactivate" value="Deactivate" /><?cs
-     else ?>
-      <input type="submit" name="activate" value="Activate" /><?cs
-     /if ?>
-    </div></form><?cs
-   /if ?>
-   <p class="path">
-    Repository path: <?cs if:config.path ?><a href="<?cs
-     var:config.browser_href ?>"><?cs
-     var:config.path ?></a><?cs else ?>&mdash;<?cs /if ?><?cs
-     if:config.min_rev || config.max_rev ?> (<?cs
-      if:config.min_rev ?>starting at <a href="<?cs
-       var:config.min_rev_href ?>">[<?cs var:config.min_rev ?>]</a><?cs
-      /if ?><?cs
-      if:config.min_rev && config.max_rev ?>, <?cs /if ?><?cs
-      if:config.max_rev ?>up to <a href="<?cs
-       var:config.max_rev_href ?>">[<?cs var:config.max_rev ?>]</a><?cs
-      /if ?>)<?cs
-     /if ?>
-   </p><?cs
-   if:config.description ?><div class="description"><?cs
-     var:config.description ?></div><?cs
-   /if ?>
-   <div id="charts"><?cs
-    each:chart = config.charts ?>
-     <object type="application/x-shockwave-flash" width="320" height="240" data="<?cs
-       var:chrome.href ?>/bitten/charts.swf">
-      <param name="movie" value="<?cs var:chrome.href ?>/bitten/charts.swf" />
-      <param name="FlashVars" value="library_path=<?cs
-        var:chrome.href ?>/bitten&amp;xml_source=<?cs
-        var:chart.href ?><?cs if:config.charts_license ?>&amp;license=<?cs
-        var:config.charts_license ?><?cs /if ?>" />
-      <param name="wmode" value="transparent" />
-     </object><br /><?cs
-    /each ?>
-   </div><?cs
-
-   if:config.can_modify || config.can_delete ?><div class="buttons"><?cs
-    if:config.can_modify ?><form method="get" action=""><div>
-     <input type="hidden" name="action" value="edit" />
-     <input type="submit" value="Edit configuration" />
-    </div></form><?cs /if ?><?cs
-    if:config.can_delete ?><form method="get" action=""><div>
-     <input type="hidden" name="action" value="delete" />
-     <input type="submit" value="Delete configuration" />
-    </div></form><?cs /if ?><?cs
-   /if ?><?cs
-   if:len(config.platforms) && len(config.builds) ?>
-    <table class="listing" id="builds"><thead><tr>
-     <th class="chgset" abbrev="Changeset">Chgset</th><?cs
-     each:platform = config.platforms ?><th><?cs var:platform.name ?><?cs
-     /each ?>
-    </tr></thead><?cs
-    if:len(config.builds) ?><tbody><?cs
-     each:rev = config.builds ?><tr>
-      <th class="chgset" scope="row"><a href="<?cs
-        var:rev.href ?>" title="View Changeset">[<?cs
-        var:name(rev) ?>]</a></th><?cs
-      each:platform = config.platforms ?><?cs
-       if:len(rev[platform.id]) ?><?cs
-        with:build = rev[platform.id] ?><td class="<?cs
-         var:build.cls ?>"><div class="info"><a href="<?cs
-         var:build.href ?>" title="View build results"><?cs var:build.id ?>:
-         <strong class="status"><?cs
-         if:build.status == 'completed' ?>Success<?cs
-         elif:build.status == 'failed' ?>Failed<?cs
-         else ?>In-progress<?cs
-         /if ?></strong></a>
-         <div class="system">
-          <strong class="ipnr"><?cs var:build.slave.name ?></strong> (<?cs
-           var:build.slave.ipnr ?>)<br />
-          <?cs var:build.slave.os.name ?> <?cs var:build.slave.os.version ?><?cs
-          if:build.slave.machine || build.slave.processor ?> / <?cs
-           alt:build.slave.processor ?><?cs
-            var:build.slave.machine ?><?cs
-           /alt ?><?cs
-          /if ?></div></div><?cs
-         if:len(build.steps) ?><ul class="steps"><?cs
-          each:step = build.steps ?><li class="<?cs
-           if:step.failed ?>failed<?cs else ?>success<?cs /if ?>">
-           <span class="duration"><?cs var:step.duration ?></span> <a href="<?cs
-           var:step.href ?>"<?cs
-           if:step.description ?> title="<?cs
-            var:step.description ?>"<?cs
-           /if ?>><?cs
-           var:name(step) ?></a><?cs
-           if:step.failed && len(step.errors) ?><ul><?cs
-            each:error = step.errors ?><li><?cs
-             var:error ?></li><?cs
-            /each ?></ul><?cs
-           /if ?></li><?cs
-          /each ?></ul><?cs
-         /if ?></td><?cs
-        /with ?><?cs
-       else ?><td>&mdash;</td><?cs
-       /if ?><?cs
-      /each ?></tr><?cs
-     /each ?></tbody><?cs
-    /if ?></table><?cs
-   /if ?></div><br style="clear: right"/><?cs
-
-  elif:page.mode == 'view_inprogress' ?><?cs
-   each:config = configs ?>
-    <h2 class="config <?cs if:!config.active ?>deactivated<?cs
-    /if ?>"><a href="<?cs var:config.href ?>"><?cs var:config.label ?></a></h2>
-     <table class="listing" id="builds"><thead><tr>
-     <th class="chgset" abbrev="Changeset">Chgset</th>
-     <th>Build</th></tr></thead><?cs 
-      each:build = config.builds ?><tr><th class="chgset" scope="row">
-        <a href="<?cs var:build.rev_href ?>" title="View Changeset">[<?cs
-        var:build.rev ?>]</a></th><td class="<?cs
-        var:build.cls ?>"><div class="info">
-        <a href="<?cs
-        var:build.href ?>" title="View build results"><?cs var:build.id ?>:
-         <strong class="status"><?cs var:build.platform ?></strong></a>
-        <div class="system">
-       <strong><?cs var:build.slave.name ?></strong> (<?cs
-       var:build.slave.ipnr ?>)<br /><?cs
-       var:build.slave.os.name ?> <?cs var:build.slave.os.version ?><?cs 
-       if:build.slave.machine || build.slave.processor ?> / <?cs
-        alt:build.slave.processor ?><?cs
-         var:build.slave.machine ?><?cs
-        /alt ?><?cs
-       /if ?><br /><?cs
-         if:build.started ?>
-           Building since: <?cs var:build.started ?>
-           (<?cs var:build.started_delta ?> ago)<?cs
-         /if ?>
-        </div>
-        </div><?cs
-         if:len(build.steps) ?><ul class="steps"><?cs
-          each:step = build.steps ?><li class="<?cs
-           if:step.failed ?>failed<?cs else ?>success<?cs /if ?>">
-           <span class="duration"><?cs var:step.duration ?></span> <a href="<?cs
-           var:step.href ?>"<?cs
-           if:step.description ?> title="<?cs
-            var:step.description ?>"<?cs
-           /if ?>><?cs
-           var:name(step) ?></a><?cs
-           if:step.failed && len(step.errors) ?><ul><?cs
-            each:error = step.errors ?><li><?cs
-             var:error ?></li><?cs
-            /each ?></ul><?cs
-           /if ?></li><?cs
-          /each ?></ul><?cs
-         /if ?></td></td></tr><?cs
-     /each ?></table><?cs
-   /each ?></div><?cs
-
-  elif:page.mode == 'edit_platform' ?>
-   <form class="platform" method="post" action="">
-    <div class="field"><label>Name:<br />
-     <input type="text" name="name" value="<?cs var:platform.name ?>" />
-    </label></div>
-    <h2>Rules</h2>
-    <table><thead><tr>
-     <th>Property name</th><th>Match pattern</th>
-    </tr></thead><tbody><?cs
-     each:rule = platform.rules ?><tr>
-      <td><input type="text" name="property_<?cs var:name(rule) ?>" value="<?cs
-       var:rule.property ?>" /></td>
-      <td><input type="text" name="pattern_<?cs var:name(rule) ?>" value="<?cs
-       var:rule.pattern ?>" /></td>
-      <td><input type="submit" name="add_rule_<?cs
-        var:name(rule) ?>" value="+" /><input type="submit" name="rm_rule_<?cs
-        var:name(rule) ?>" value="-" />
-      </td>
-     </tr><?cs /each ?>
-    </tbody></table>
-    <div class="buttons">
-     <form method="get" action=""><div>
-     <input type="hidden" name="action" value="<?cs
-       if:platform.exists ?>edit<?cs else ?>new<?cs /if ?>" />
-      <input type="hidden" name="platform" value="<?cs
-       var:platform.id ?>" />
-      <input type="submit" value="<?cs
-       if:platform.exists ?>Save changes<?cs else ?>Add platform<?cs
-       /if ?>" />
-      <input type="submit" name="cancel" value="Cancel" />
-     </div></form>
-    </div>
-   </form><?cs
-
-  /if ?>
- </div>
-<?cs include:"footer.cs" ?>
new file mode 100755
--- /dev/null
+++ b/bitten/templates/bitten_config.html
@@ -0,0 +1,178 @@
+<!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:xi="http://www.w3.org/2001/XInclude"
+      xmlns:py="http://genshi.edgewall.org/">
+  <xi:include href="layout.html" />
+  <head>
+    <title>$title</title>
+  </head>
+  <body>
+    <strong py:def="build_status(status)" class="status">
+      <py:choose test="status">
+      <py:when test="'completed'">Success</py:when>
+      <py:when test="'failed'">Failed</py:when>
+      <py:when test="'in progress'">In-progress</py:when>
+      </py:choose>
+    </strong>
+    
+    <div py:def="slave_info(slave)" class="system">
+      <strong>$slave.name</strong> ($slave.ipnr)<br />
+      $slave.os_name $slave.os_version
+      <py:if test="slave.machine or slave.processor"> / </py:if>
+      ${slave.processor or slave.machine or ''}
+    </div>
+    
+    <ul py:def="build_steps(steps)" py:if="steps" class="steps">
+      <li py:for="step in steps"
+          class="${step.failed and 'failed' or 'success'}">
+        <span class="duration">$step.duration</span> 
+        <a href="$step.href" title="${step.description or None}">
+          $step.name
+        </a>
+        <ul py:if="step.failed and step.errors">
+          <li py:for="error in step.errors">$error</li>
+        </ul>
+      </li>
+    </ul>
+ 
+    <div id="content" class="build">
+      <h1>$title</h1><py:choose test="page_mode"><py:when test="'overview'">
+      <form id="prefs" method="get" action="">
+        <div>
+          <input type="checkbox" id="showall" name="show" value="all"
+                 checked="${show_all and 'checked' or None}" />
+          <label for="showall">Show deactivated configurations</label>
+        </div>
+        <div class="buttons">
+          <input type="submit" value="Update" />
+        </div>
+      </form><py:for each="config in configs">
+      <h2 class="config ${not config.active and 'deactivated' or ''}">
+        <a href="$config.href">$config.label</a>
+      </h2>
+      <div py:if="config.description" class="description">
+        $config.description
+      </div><py:if test="len(config.builds)">
+      <h3 class="builds"><a href="$config.href">Latest builds</a></h3>
+      <table class="builds"><tbody><tr>
+        <th py:with="youngest_rev = config.youngest_rev">
+          <a href="$youngest_rev.href">[$youngest_rev.id]</a>
+          by $youngest_rev.author<p class="date">$youngest_rev.date</p>
+          <p class="message">$youngest_rev.message</p>
+        </th>
+        <td py:for="build in config.builds" class="$build.cls"><py:choose>
+        <py:when test="build.status != 'pending'">
+          <a href="$build.href">$build.platform</a>
+          <p class="date">$build.stopped</p>
+          ${slave_info(build.slave)}
+          ${build_status(build.status)}
+        </py:when><py:otherwise>
+          <strong>$build.platform</strong>
+          <p class="nobuild">No build yet</p>
+        </py:otherwise></py:choose>
+        </td>
+      </tr></tbody></table></py:if></py:for>
+
+    </py:when><py:when test="'view_config'">
+      <form py:if="config.can_modify" id="prefs" method="post"
+            class="activation"><py:choose>
+        <div py:when="not config.active" class="help">
+          This build configuration is currently inactive.<br />
+          No builds will be initiated for this configuration<br />
+          until it is activated.
+        </div>
+        <div py:otherwise="" class="help">
+          This configuration is currently active.
+        </div></py:choose>
+        <div class="buttons" py:choose="">
+          <input type="hidden" name="action" value="edit" />
+          <input py:when="config.active" type="submit" name="deactivate"
+                 value="Deactivate" />
+          <input py:otherwise="" type="submit" name="activate"
+                 value="Activate" />
+        </div>
+      </form>
+      <p class="path">
+        Repository path: 
+        <a py:if="config.path" href="$config.browser_href">$config.path</a>
+        ${not config.path and '&mdash;' or ''}
+        <py:if test="config.min_rev or config.max_rev">
+        (<py:if test="config.min_rev">starting at 
+         <a href="$config.min_rev_href">[$config.min_rev]</a></py:if>
+         <py:if test="config.min_rev and config.max_rev">, </py:if>
+         <py:if test="config.max_rev">up to 
+         <a href="$config.max_rev_href">[$config.max_rev]</a></py:if>)
+        </py:if>
+      </p>
+      <div py:if="config.description" class="description">
+        $config.description
+      </div>
+      <div id="charts"><py:for each="chart in config.charts">
+        <object type="application/x-shockwave-flash"
+                width="320" height="240"
+                data="${href.chrome('bitten', 'charts.swf')}">
+          <param name="movie" value="${href.chrome('bitten', 'charts.swf')}" />
+          <param name="FlashVars"
+                 value="library_path=${href.chrome('bitten')}&amp;xml_source=$chart.href${config.charts_license and '&amp;license='+config.charts_license or ''}" />
+          <param name="wmode" value="transparent" />
+        </object><br /></py:for>
+      </div>
+
+      <table py:if="config.platforms and config.builds"
+             class="listing" id="builds">
+        <thead><tr>
+          <th class="chgset"><abbr title="Changeset">Chgset</abbr></th>
+          <th py:for="platform in config.platforms">$platform.name</th>
+        </tr></thead>
+        <tbody py:if="config.builds">
+          <tr py:for="rev_num in sorted(config.builds, reverse=True)"
+              py:with="rev = config.builds[rev_num]">
+            <th class="chgset" scope="row">
+              <a href="$rev.href" title="View Changeset">[$rev_num]</a>
+            </th><py:for each="platform in config.platforms"><py:choose>
+            <td py:when="platform.id in rev" py:with="build = rev[platform.id]"
+                class="$build.cls">
+              <div class="info">
+                <a href="$build.href" title="View build results">
+                  $build.id
+                  ${build_status(build.status)}
+                </a>
+                ${slave_info(build.slave)}
+              </div>
+              ${build_steps(build.steps)}
+            </td>
+            <td py:otherwise="">&mdash;</td></py:choose></py:for>
+          </tr>
+        </tbody>
+      </table>
+      <br style="clear: right"/>
+
+    </py:when><py:when test="'view-inprogress'">
+      <py:for each="config in configs">
+      <h2 class="config ${not config.active and 'deactivated' or ''}">
+        <a href="$config.href">$config.label</a>
+      </h2>
+      <table class="listing" id="builds">
+        <thead><tr>
+          <th class="chgset" abbrev="Changeset">Chgset</th><th>Build</th>
+        </tr></thead><tbody>
+        <tr py:for="build in config.builds">
+          <th class="chgset" scope="row">
+            <a href="$build.rev_href" title="View Changeset">[$build.rev]</a>
+          </th>
+          <td class="$build.cls">
+            <div class="info">
+              <a href="$build.href" title="View build results">
+                $build.id: <strong class="status">$build.platform</strong>
+              </a>
+              ${slave_info(build.slave)}
+            </div>
+            ${build_steps(build.steps)}
+          </td>
+        </tr></tbody>
+      </table></py:for></py:when>
+    </py:choose></div>
+  </body>
+</html>
deleted file mode 100644
--- a/bitten/templates/bitten_summary_coverage.cs
+++ /dev/null
@@ -1,16 +0,0 @@
-<h3>Code Coverage</h3>
-<table class="listing coverage">
- <thead><tr><th class="name">Unit</th><th class="loc">Lines of Code</th>
- <th class="cov">Coverage</th></tr></thead>
- <tbody><?cs
- each:item = data ?><tr><td class="name"><?cs
-  if:item.href ?><a href="<?cs var:item.href ?>"><?cs var:item.name ?></a><?cs
-  else ?><?cs var:item.name ?><?cs
-  /if ?></td><td class="loc"><?cs
-  var:item.loc ?></td><td class="cov"><?cs var:item.cov ?>%</td></tr><?cs
- /each ?></tbody>
- <tbody class="totals"><tr>
-  <th>Total</th><td><?cs var:totals.loc ?></td>
-  <td><?cs var:totals.cov ?>%</td>
- </tr></tbody>
-</table>
new file mode 100755
--- /dev/null
+++ b/bitten/templates/bitten_summary_coverage.html
@@ -0,0 +1,27 @@
+<!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/" py:strip="">
+ <body py:strip="">
+  <h3>Code Coverage</h3>
+  <table class="listing coverage">
+   <thead><tr>
+    <th class="name">Unit</th><th class="loc">Lines of Code</th>
+    <th class="cov">Coverage</th>
+   </tr></thead>
+   <tbody><tr py:for="item in data.units">
+    <td class="name" py:choose="">
+     <a py:when="item.href" href="$item.href">$item.name</a>
+     <py:otherwise>$item.name</py:otherwise>
+    </td>
+    <td class="loc">$item.loc</td>
+    <td class="cov">${item.cov}%</td>
+   </tr></tbody>
+   <tbody class="totals"><tr py:with="totals = data.totals">
+    <th>Total</th><td>$totals.loc</td>
+    <td>${totals.cov}%</td>
+   </tr></tbody>
+  </table>
+ </body>
+</html>
deleted file mode 100644
--- a/bitten/templates/bitten_summary_tests.cs
+++ /dev/null
@@ -1,23 +0,0 @@
-<h3>Test Results</h3>
-<table class="listing tests">
- <thead><tr>
-  <th>Test Fixture</th><th>Total</th>
-  <th>Failures</th><th>Errors</th>
- </tr></thead>
- <tbody><?cs
- each:item = data ?><tr<?cs
-  if:item.num_failure != 0 || item.num_error != 0 ?> class="failed"<?cs
-  /if ?>><th><?cs
-  if:item.href ?><a href="<?cs var:item.href ?>"><?cs var:item.name ?></a><?cs
-  else ?><?cs var:item.name ?><?cs
-  /if ?></th><td><?cs
-  var:#item.num_success + #item.num_failure + #item.num_error ?></td><td><?cs
-  var:item.num_failure ?></td><td><?cs
-  var:item.num_error ?></td></tr><?cs
- /each ?></tbody>
- <tbody class="totals"><tr>
-  <th>Total</th>
-  <td><?cs var:totals.success ?></td>
-  <td><?cs var:totals.failure ?></td><td><?cs var:totals.error ?></td>
- </tr></tbody>
-</table>
new file mode 100755
--- /dev/null
+++ b/bitten/templates/bitten_summary_tests.html
@@ -0,0 +1,31 @@
+<!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/" py:strip="">
+ <body py:strip="">
+  <h3>Test Results</h3>
+  <table class="listing tests">
+   <thead><tr>
+    <th>Test Fixture</th><th>Total</th>
+    <th>Failures</th><th>Errors</th>
+   </tr></thead>
+   <tbody><tr py:for="item in data.fixtures"
+              class="${item.num_failure or item.num_error and 'failed' or None}">
+    <th py:choose="">
+     <a py:when="item.href" href="$item.href">$item.name</a>
+     <py:otherwise>$item.name</py:otherwise>
+    </th>
+    <td>${item.num_success + item.num_failure + item.num_error}</td>
+    <td>$item.num_failure</td>
+    <td>$item.num_error</td>
+   </tr></tbody>
+   <tbody class="totals"><tr py:with="totals = data.totals">
+    <th>Total</th>
+    <td>$totals.success</td>
+    <td>$totals.failure</td>
+    <td>$totals.error</td>
+   </tr></tbody>
+  </table>
+ </body>
+</html>
--- a/bitten/tests/admin.py
+++ b/bitten/tests/admin.py
@@ -63,36 +63,33 @@
             DefaultPermissionPolicy.CACHE_EXPIRY = self.old_perm_cache_expiry
         shutil.rmtree(self.env.path)
 
-    def test_get_admin_pages(self):
+    def test_get_admin_panels(self):
         provider = BuildMasterAdminPageProvider(self.env)
 
         req = Mock(perm=PermissionCache(self.env, 'joe'))
         self.assertEqual([('bitten', 'Builds', 'master', 'Master Settings')],
-                         list(provider.get_admin_pages(req)))
+                         list(provider.get_admin_panels(req)))
 
         PermissionSystem(self.env).revoke_permission('joe', 'BUILD_ADMIN')
         req = Mock(perm=PermissionCache(self.env, 'joe'))
-        self.assertEqual([], list(provider.get_admin_pages(req)))
+        self.assertEqual([], list(provider.get_admin_panels(req)))
 
     def test_process_get_request(self):
-        data = {}
-        req = Mock(method='GET', chrome={}, hdf=data, href=Href('/'),
+        req = Mock(method='GET', chrome={}, href=Href('/'),
                    perm=PermissionCache(self.env, 'joe'))
 
         provider = BuildMasterAdminPageProvider(self.env)
-        template_name, content_type = provider.process_admin_request(
+        template_name, data = provider.render_admin_panel(
             req, 'bitten', 'master', ''
         )
 
-        self.assertEqual('bitten_admin_master.cs', template_name)
-        self.assertEqual(None, content_type)
-        assert 'admin.master' in data
-        self.assertEqual({
-            'slave_timeout': 3600,
-            'stabilize_wait': 0,
-            'adjust_timestamps': False,
-            'build_all': False,
-        }, data['admin.master'])
+        self.assertEqual('bitten_admin_master.html', template_name)
+        assert 'master' in data
+        master = data['master']
+        self.assertEqual(3600, master.slave_timeout)
+        self.assertEqual(0, master.stabilize_wait)
+        assert not master.adjust_timestamps
+        assert not master.build_all
 
     def test_process_config_changes(self):
         redirected_to = []
@@ -105,7 +102,7 @@
 
         provider = BuildMasterAdminPageProvider(self.env)
         try:
-            provider.process_admin_request(req, 'bitten', 'master', '')
+            provider.render_admin_panel(req, 'bitten', 'master', '')
             self.fail('Expected RequestDone')
 
         except RequestDone:
@@ -155,30 +152,28 @@
             DefaultPermissionPolicy.CACHE_EXPIRY = self.old_perm_cache_expiry
         shutil.rmtree(self.env.path)
 
-    def test_get_admin_pages(self):
+    def test_get_admin_panels(self):
         provider = BuildConfigurationsAdminPageProvider(self.env)
 
         req = Mock(perm=PermissionCache(self.env, 'joe'))
         self.assertEqual([('bitten', 'Builds', 'configs', 'Configurations')],
-                         list(provider.get_admin_pages(req)))
+                         list(provider.get_admin_panels(req)))
 
         PermissionSystem(self.env).revoke_permission('joe', 'BUILD_MODIFY')
         req = Mock(perm=PermissionCache(self.env, 'joe'))
-        self.assertEqual([], list(provider.get_admin_pages(req)))
+        self.assertEqual([], list(provider.get_admin_panels(req)))
 
     def test_process_view_configs_empty(self):
-        data = {}
-        req = Mock(method='GET', chrome={}, hdf=data, href=Href('/'),
+        req = Mock(method='GET', chrome={}, href=Href('/'),
                    perm=PermissionCache(self.env, 'joe'))
 
         provider = BuildConfigurationsAdminPageProvider(self.env)
-        template_name, content_type = provider.process_admin_request(
+        template_name, data = provider.render_admin_panel(
             req, 'bitten', 'configs', ''
         )
 
-        self.assertEqual('bitten_admin_configs.cs', template_name)
-        self.assertEqual(None, content_type)
-        self.assertEqual([], data['admin']['configs'])
+        self.assertEqual('bitten_admin_configs.html', template_name)
+        self.assertEqual([], data['configs'])
 
     def test_process_view_configs(self):
         BuildConfig(self.env, name='foo', label='Foo', path='branches/foo',
@@ -186,18 +181,17 @@
         BuildConfig(self.env, name='bar', label='Bar', path='branches/bar',
                     min_rev='123', max_rev='456').insert()
 
-        data = {}
-        req = Mock(method='GET', chrome={}, hdf=data, href=Href('/'),
+        req = Mock(method='GET', chrome={}, href=Href('/'),
                    perm=PermissionCache(self.env, 'joe'))
 
         provider = BuildConfigurationsAdminPageProvider(self.env)
-        template_name, content_type = provider.process_admin_request(
+        template_name, data = provider.render_admin_panel(
             req, 'bitten', 'configs', ''
         )
 
-        self.assertEqual('bitten_admin_configs.cs', template_name)
-        self.assertEqual(None, content_type)
-        configs = data['admin']['configs']
+        self.assertEqual('bitten_admin_configs.html', template_name)
+        assert 'configs' in data
+        configs = data['configs']
         self.assertEqual(2, len(configs))
         self.assertEqual({
             'name': 'bar', 'href': '/admin/bitten/configs/bar',
@@ -215,18 +209,17 @@
                     active=True).insert()
         TargetPlatform(self.env, config='foo', name='any').insert()
 
-        data = {}
-        req = Mock(method='GET', chrome={}, hdf=data, href=Href('/'),
+        req = Mock(method='GET', chrome={}, href=Href('/'),
                    perm=PermissionCache(self.env, 'joe'))
 
         provider = BuildConfigurationsAdminPageProvider(self.env)
-        template_name, content_type = provider.process_admin_request(
+        template_name, data = provider.render_admin_panel(
             req, 'bitten', 'configs', 'foo'
         )
 
-        self.assertEqual('bitten_admin_configs.cs', template_name)
-        self.assertEqual(None, content_type)
-        config = data['admin']['config']
+        self.assertEqual('bitten_admin_configs.html', template_name)
+        assert 'config' in data
+        config = data['config']
         self.assertEqual({
             'name': 'foo', 'label': 'Foo', 'description': '', 'recipe': '',
             'path': 'branches/foo', 'min_rev': None, 'max_rev': None,
@@ -251,7 +244,7 @@
 
         provider = BuildConfigurationsAdminPageProvider(self.env)
         try:
-            provider.process_admin_request(req, 'bitten', 'configs', '')
+            provider.render_admin_panel(req, 'bitten', 'configs', '')
             self.fail('Expected RequestDone')
 
         except RequestDone:
@@ -277,7 +270,7 @@
 
         provider = BuildConfigurationsAdminPageProvider(self.env)
         try:
-            provider.process_admin_request(req, 'bitten', 'configs', '')
+            provider.render_admin_panel(req, 'bitten', 'configs', '')
             self.fail('Expected RequestDone')
 
         except RequestDone:
@@ -303,7 +296,7 @@
 
         provider = BuildConfigurationsAdminPageProvider(self.env)
         try:
-            provider.process_admin_request(req, 'bitten', 'configs', '')
+            provider.render_admin_panel(req, 'bitten', 'configs', '')
             self.fail('Expected RequestDone')
 
         except RequestDone:
@@ -323,7 +316,7 @@
 
         provider = BuildConfigurationsAdminPageProvider(self.env)
         try:
-            provider.process_admin_request(req, 'bitten', 'configs', '')
+            provider.render_admin_panel(req, 'bitten', 'configs', '')
             self.fail('Expected RequestDone')
 
         except RequestDone:
@@ -338,7 +331,7 @@
 
         provider = BuildConfigurationsAdminPageProvider(self.env)
         try:
-            provider.process_admin_request(req, 'bitten', 'configs', '')
+            provider.render_admin_panel(req, 'bitten', 'configs', '')
             self.fail('Expected TracError')
 
         except TracError, e:
@@ -351,7 +344,7 @@
 
         provider = BuildConfigurationsAdminPageProvider(self.env)
         try:
-            provider.process_admin_request(req, 'bitten', 'configs', '')
+            provider.render_admin_panel(req, 'bitten', 'configs', '')
             self.fail('Expected TracError')
 
         except TracError, e:
@@ -370,7 +363,7 @@
 
         provider = BuildConfigurationsAdminPageProvider(self.env)
         try:
-            provider.process_admin_request(req, 'bitten', 'configs', '')
+            provider.render_admin_panel(req, 'bitten', 'configs', '')
             self.fail('Expected TracError')
 
         except TracError, e:
@@ -387,7 +380,7 @@
                    args={'add': '', 'name': 'bar', 'label': 'Bar'})
 
         provider = BuildConfigurationsAdminPageProvider(self.env)
-        self.assertRaises(PermissionError, provider.process_admin_request, req,
+        self.assertRaises(PermissionError, provider.render_admin_panel, req,
                           'bitten', 'configs', '')
 
     def test_process_remove_config(self):
@@ -406,7 +399,7 @@
 
         provider = BuildConfigurationsAdminPageProvider(self.env)
         try:
-            provider.process_admin_request(req, 'bitten', 'configs', '')
+            provider.render_admin_panel(req, 'bitten', 'configs', '')
             self.fail('Expected RequestDone')
 
         except RequestDone:
@@ -430,7 +423,7 @@
 
         provider = BuildConfigurationsAdminPageProvider(self.env)
         try:
-            provider.process_admin_request(req, 'bitten', 'configs', '')
+            provider.render_admin_panel(req, 'bitten', 'configs', '')
             self.fail('Expected RequestDone')
 
         except RequestDone:
@@ -448,7 +441,7 @@
 
         provider = BuildConfigurationsAdminPageProvider(self.env)
         try:
-            provider.process_admin_request(req, 'bitten', 'configs', '')
+            provider.render_admin_panel(req, 'bitten', 'configs', '')
             self.fail('Expected TracError')
 
         except TracError, e:
@@ -463,7 +456,7 @@
 
         provider = BuildConfigurationsAdminPageProvider(self.env)
         try:
-            provider.process_admin_request(req, 'bitten', 'configs', '')
+            provider.render_admin_panel(req, 'bitten', 'configs', '')
             self.fail('Expected TracError')
 
         except TracError, e:
@@ -479,7 +472,7 @@
                    args={'remove': '', 'sel': 'bar'})
 
         provider = BuildConfigurationsAdminPageProvider(self.env)
-        self.assertRaises(PermissionError, provider.process_admin_request, req,
+        self.assertRaises(PermissionError, provider.render_admin_panel, req,
                           'bitten', 'configs', '')
 
     def test_process_update_config(self):
@@ -499,7 +492,7 @@
 
         provider = BuildConfigurationsAdminPageProvider(self.env)
         try:
-            provider.process_admin_request(req, 'bitten', 'configs', 'foo')
+            provider.render_admin_panel(req, 'bitten', 'configs', 'foo')
             self.fail('Expected RequestDone')
 
         except RequestDone:
@@ -518,7 +511,7 @@
 
         provider = BuildConfigurationsAdminPageProvider(self.env)
         try:
-            provider.process_admin_request(req, 'bitten', 'configs', 'foo')
+            provider.render_admin_panel(req, 'bitten', 'configs', 'foo')
             self.fail('Expected TracError')
 
         except TracError, e:
@@ -534,7 +527,7 @@
 
         provider = BuildConfigurationsAdminPageProvider(self.env)
         try:
-            provider.process_admin_request(req, 'bitten', 'configs', 'foo')
+            provider.render_admin_panel(req, 'bitten', 'configs', 'foo')
             self.fail('Expected TracError')
 
         except TracError, e:
@@ -556,7 +549,7 @@
 
         provider = BuildConfigurationsAdminPageProvider(self.env)
         try:
-            provider.process_admin_request(req, 'bitten', 'configs', 'foo')
+            provider.render_admin_panel(req, 'bitten', 'configs', 'foo')
             self.fail('Expected TracError')
 
         except TracError, e:
@@ -573,7 +566,7 @@
 
         provider = BuildConfigurationsAdminPageProvider(self.env)
         try:
-            provider.process_admin_request(req, 'bitten', 'configs', 'foo')
+            provider.render_admin_panel(req, 'bitten', 'configs', 'foo')
             self.fail('Expected TracError')
 
         except TracError, e:
@@ -592,7 +585,7 @@
 
         provider = BuildConfigurationsAdminPageProvider(self.env)
         try:
-            provider.process_admin_request(req, 'bitten', 'configs', 'foo')
+            provider.render_admin_panel(req, 'bitten', 'configs', 'foo')
             self.fail('Expected TracError')
 
         except TracError, e:
@@ -609,13 +602,13 @@
                    args={'new': ''})
 
         provider = BuildConfigurationsAdminPageProvider(self.env)
-        template_name, content_type = provider.process_admin_request(
+        template_name, data = provider.render_admin_panel(
             req, 'bitten', 'configs', 'foo'
         )
 
-        self.assertEqual('bitten_admin_configs.cs', template_name)
-        self.assertEqual(None, content_type)
-        platform = data['admin']['platform']
+        self.assertEqual('bitten_admin_configs.html', template_name)
+        assert 'platform' in data
+        platform = data['platform']
         self.assertEqual({
             'id': None, 'exists': False, 'name': None, 'rules': [('', '')],
         }, platform)
@@ -636,7 +629,7 @@
 
         provider = BuildConfigurationsAdminPageProvider(self.env)
         try:
-            provider.process_admin_request(req, 'bitten', 'configs', 'foo')
+            provider.render_admin_panel(req, 'bitten', 'configs', 'foo')
             self.fail('Expected RequestDone')
 
         except RequestDone:
@@ -663,7 +656,7 @@
 
         provider = BuildConfigurationsAdminPageProvider(self.env)
         try:
-            provider.process_admin_request(req, 'bitten', 'configs', 'foo')
+            provider.render_admin_panel(req, 'bitten', 'configs', 'foo')
             self.fail('Expected RequestDone')
 
         except RequestDone:
@@ -689,7 +682,7 @@
 
         provider = BuildConfigurationsAdminPageProvider(self.env)
         try:
-            provider.process_admin_request(req, 'bitten', 'configs', 'foo')
+            provider.render_admin_panel(req, 'bitten', 'configs', 'foo')
             self.fail('Expected RequestDone')
 
         except RequestDone:
@@ -715,7 +708,7 @@
 
         provider = BuildConfigurationsAdminPageProvider(self.env)
         try:
-            provider.process_admin_request(req, 'bitten', 'configs', 'foo')
+            provider.render_admin_panel(req, 'bitten', 'configs', 'foo')
             self.fail('Expected TracError')
 
         except TracError, e:
@@ -727,18 +720,17 @@
         platform = TargetPlatform(self.env, config='foo', name='any')
         platform.insert()
 
-        data = {}
-        req = Mock(method='GET', chrome={}, hdf=data, href=Href('/'),
+        req = Mock(method='GET', chrome={}, href=Href('/'),
                    perm=PermissionCache(self.env, 'joe'), args={})
 
         provider = BuildConfigurationsAdminPageProvider(self.env)
-        template_name, content_type = provider.process_admin_request(
+        template_name, data = provider.render_admin_panel(
             req, 'bitten', 'configs', 'foo/%d' % platform.id
         )
 
-        self.assertEqual('bitten_admin_configs.cs', template_name)
-        self.assertEqual(None, content_type)
-        platform = data['admin']['platform']
+        self.assertEqual('bitten_admin_configs.html', template_name)
+        assert 'platform' in data
+        platform = data['platform']
         self.assertEqual({
             'id': 1, 'exists': True, 'name': 'any', 'rules': [('', '')],
         }, platform)
@@ -761,7 +753,7 @@
 
         provider = BuildConfigurationsAdminPageProvider(self.env)
         try:
-            provider.process_admin_request(req, 'bitten', 'configs',
+            provider.render_admin_panel(req, 'bitten', 'configs',
                                            'foo/%d' % platform.id)
             self.fail('Expected RequestDone')
 
@@ -791,7 +783,7 @@
 
         provider = BuildConfigurationsAdminPageProvider(self.env)
         try:
-            provider.process_admin_request(req, 'bitten', 'configs',
+            provider.render_admin_panel(req, 'bitten', 'configs',
                                            'foo/%d' % platform.id)
             self.fail('Expected RequestDone')
 
--- a/bitten/tests/web_ui.py
+++ b/bitten/tests/web_ui.py
@@ -17,7 +17,6 @@
 from trac.perm import PermissionCache, PermissionSystem
 from trac.test import EnvironmentStub, Mock
 from trac.util.html import Markup
-from trac.web.clearsilver import HDFWrapper
 from trac.web.href import Href
 from bitten.main import BuildSystem
 from bitten.model import Build, BuildConfig, BuildStep, TargetPlatform, schema
@@ -58,14 +57,13 @@
         PermissionSystem(self.env).grant_permission('joe', 'BUILD_VIEW')
         req = Mock(method='GET', base_path='', cgi_location='',
                    path_info='/build', href=Href('/trac'), args={}, chrome={},
-                   hdf=HDFWrapper(), perm=PermissionCache(self.env, 'joe'))
+                   perm=PermissionCache(self.env, 'joe'))
 
         module = BuildConfigController(self.env)
         assert module.match_request(req)
-        module.process_request(req)
+        _, data, _ = module.process_request(req)
 
-        self.assertEqual('overview', req.hdf['page.mode'])
-        self.assertEqual('0', req.hdf.get('build.can_create', '0'))
+        self.assertEqual('overview', data['page_mode'])
 
     def test_view_config(self):
         config = BuildConfig(self.env, name='test', path='trunk')
@@ -76,7 +74,7 @@
         PermissionSystem(self.env).grant_permission('joe', 'BUILD_VIEW')
         req = Mock(method='GET', base_path='', cgi_location='',
                    path_info='/build/test', href=Href('/trac'), args={},
-                   chrome={}, hdf=HDFWrapper(), authname='joe',
+                   chrome={}, authname='joe',
                    perm=PermissionCache(self.env, 'joe'))
 
         root = Mock(get_entries=lambda: ['foo'],
@@ -87,12 +85,10 @@
 
         module = BuildConfigController(self.env)
         assert module.match_request(req)
-        module.process_request(req)
+        _, data, _ = module.process_request(req)
 
-        self.assertEqual('view_config', req.hdf['page.mode'])
-        self.assertEqual('0', req.hdf.get('build.config.can_delete', '0'))
-        self.assertEqual('0', req.hdf.get('build.config.can_modify', '0'))
-        self.assertEqual(None, req.hdf.get('chrome.links.next.0.href'))
+        self.assertEqual('view_config', data['page_mode'])
+        assert not 'next' in req.chrome['links']
 
     def test_view_config_paging(self):
         config = BuildConfig(self.env, name='test', path='trunk')
@@ -103,7 +99,7 @@
         PermissionSystem(self.env).grant_permission('joe', 'BUILD_VIEW')
         req = Mock(method='GET', base_path='', cgi_location='',
                    path_info='/build/test', href=Href('/trac'), args={},
-                   chrome={}, hdf=HDFWrapper(), authname='joe',
+                   chrome={}, authname='joe',
                    perm=PermissionCache(self.env, 'joe'))
 
         root = Mock(get_entries=lambda: ['foo'],
@@ -114,14 +110,11 @@
 
         module = BuildConfigController(self.env)
         assert module.match_request(req)
-        module.process_request(req)
+        _, data, _ = module.process_request(req)
 
-        if req.chrome: # Trac 0.11
+        if req.chrome:
             self.assertEqual('/trac/build/test?page=2',
                              req.chrome['links']['next'][0]['href'])
-        else:
-            self.assertEqual('/trac/build/test?page=2',
-                             req.hdf.get('chrome.links.next.0.href'))
 
 
 class SourceFileLinkFormatterTestCase(unittest.TestCase):
--- a/bitten/util/testrunner.py
+++ b/bitten/util/testrunner.py
@@ -128,8 +128,9 @@
         test.finalize_options(self)
 
         if self.xml_output is not None:
-            if not os.path.exists(os.path.dirname(self.xml_output)):
-                os.makedirs(os.path.dirname(self.xml_output))
+            output_dir = os.path.dirname(self.xml_output) or '.'
+            if not os.path.exists(output_dir):
+                os.makedirs(output_dir)
             self.xml_output_file = open(self.xml_output, 'w')
 
         if self.coverage_method not in ('trace', 'coverage', 'figleaf'):
--- a/bitten/util/xmlio.py
+++ b/bitten/util/xmlio.py
@@ -20,6 +20,17 @@
     from StringIO import StringIO
 from UserDict import DictMixin
 
+import cgi
+import string
+
+__trans = string.maketrans ("", "")
+__todel = ""
+for c in range (0, 256):
+    c1 = chr (c)
+    if not c1 in string.printable:
+        __todel += c1
+del c, c1
+
 __all__ = ['Fragment', 'Element', 'ParsedElement', 'parse']
 __docformat__ = 'restructuredtext en'
 
@@ -27,8 +38,7 @@
     """Escape special characters in the provided text so that it can be safely
     included in XML text nodes.
     """
-    return str(text).replace('&', '&amp;').replace('<', '&lt;') \
-                    .replace('>', '&gt;')
+    return cgi.escape (str(text)).translate (__trans, __todel)
 
 def _escape_attr(attr):
     """Escape special characters in the provided text so that it can be safely
--- a/bitten/web_ui.py
+++ b/bitten/web_ui.py
@@ -16,65 +16,51 @@
 from StringIO import StringIO
 
 import pkg_resources
+from genshi.builder import tag
 from trac.core import *
-try:
-    from trac.timeline import ITimelineEventProvider
-    have_trac_011 = True
-except ImportError:
-    from trac.Timeline import ITimelineEventProvider
-    have_trac_011 = False
+from trac.timeline import ITimelineEventProvider
 from trac.util import escape, pretty_timedelta, format_datetime, shorten_line, \
                       Markup
 from trac.util.html import html
 from trac.web import IRequestHandler
 from trac.web.chrome import INavigationContributor, ITemplateProvider, \
-                            add_link, add_stylesheet
-from trac.wiki import wiki_to_html
-from trac.wiki import wiki_to_oneliner as wiki_to_oneliner_
+                            add_link, add_stylesheet, add_ctxtnav, \
+                            prevnext_nav, add_script
+from trac.wiki import wiki_to_html, wiki_to_oneliner
 from bitten.api import ILogFormatter, IReportChartGenerator, IReportSummarizer
 from bitten.model import BuildConfig, TargetPlatform, Build, BuildStep, \
                          BuildLog, Report
 from bitten.queue import collect_changes
 
-def wiki_to_oneliner(wikitext, env, db=None, shorten=False, absurls=False,
-                      req=None):
-    if have_trac_011:
-        return wiki_to_oneliner_(wikitext, env, db=db, shorten=shorten,
-                                 absurls=absurls, req=req)
-    else:
-        return wiki_to_oneliner_(wikitext, env, db=db, shorten=shorten,
-                                 absurls=absurls)
-
-
 _status_label = {Build.PENDING: 'pending',
                  Build.IN_PROGRESS: 'in progress',
                  Build.SUCCESS: 'completed',
                  Build.FAILURE: 'failed'}
 
-def _build_to_hdf(env, req, build):
-    hdf = {'id': build.id, 'name': build.slave, 'rev': build.rev,
-           'status': _status_label[build.status],
-           'cls': _status_label[build.status].replace(' ', '-'),
-           'href': req.href.build(build.config, build.id),
-           'chgset_href': req.href.changeset(build.rev)}
+def _get_build_data(env, req, build):
+    data = {'id': build.id, 'name': build.slave, 'rev': build.rev,
+            'status': _status_label[build.status],
+            'cls': _status_label[build.status].replace(' ', '-'),
+            'href': req.href.build(build.config, build.id),
+            'chgset_href': req.href.changeset(build.rev)}
     if build.started:
-        hdf['started'] = format_datetime(build.started)
-        hdf['started_delta'] = pretty_timedelta(build.started)
-        hdf['duration'] = pretty_timedelta(build.started)
+        data['started'] = format_datetime(build.started)
+        data['started_delta'] = pretty_timedelta(build.started)
+        data['duration'] = pretty_timedelta(build.started)
     if build.stopped:
-        hdf['stopped'] = format_datetime(build.stopped)
-        hdf['stopped_delta'] = pretty_timedelta(build.stopped)
-        hdf['duration'] = pretty_timedelta(build.stopped, build.started)
-    hdf['slave'] = {
+        data['stopped'] = format_datetime(build.stopped)
+        data['stopped_delta'] = pretty_timedelta(build.stopped)
+        data['duration'] = pretty_timedelta(build.stopped, build.started)
+    data['slave'] = {
         'name': build.slave,
         'ipnr': build.slave_info.get(Build.IP_ADDRESS),
-        'os.name': build.slave_info.get(Build.OS_NAME),
-        'os.family': build.slave_info.get(Build.OS_FAMILY),
-        'os.version': build.slave_info.get(Build.OS_VERSION),
+        'os_name': build.slave_info.get(Build.OS_NAME),
+        'os_family': build.slave_info.get(Build.OS_FAMILY),
+        'os_version': build.slave_info.get(Build.OS_VERSION),
         'machine': build.slave_info.get(Build.MACHINE),
         'processor': build.slave_info.get(Build.PROCESSOR)
     }
-    return hdf
+    return data
 
 
 class BittenChrome(Component):
@@ -95,11 +81,9 @@
     def get_navigation_items(self, req):
         """Return the navigation item for access the build status overview from
         the Trac navigation bar."""
-        if not req.perm.has_permission('BUILD_VIEW'):
-            return
-        yield ('mainnav', 'build', \
-               Markup('<a href="%s" accesskey="5">Build Status</a>') %
-                      req.href.build())
+        if 'BUILD_VIEW' in req.perm:
+            yield ('mainnav', 'build',
+                   tag.a('Builds Status', href=req.href.build(), accesskey=5))
 
     # ITemplatesProvider methods
 
@@ -127,43 +111,44 @@
             return True
 
     def process_request(self, req):
-        req.perm.assert_permission('BUILD_VIEW')
+        req.perm.require('BUILD_VIEW')
 
         action = req.args.get('action')
         view = req.args.get('view')
         config = req.args.get('config')
 
         if config:
-            self._render_config(req, config)
+            data = self._render_config(req, config)
         elif view == 'inprogress':
-            self._render_inprogress(req)
+            data = self._render_inprogress(req)
         else:
-            self._render_overview(req)
+            data = self._render_overview(req)
 
         add_stylesheet(req, 'bitten/bitten.css')
-        return 'bitten_config.cs', None
+        return 'bitten_config.html', data, None
 
     # Internal methods
 
     def _render_overview(self, req):
-        req.hdf['title'] = 'Build Status'
+        data = {'title': 'Build Status'}
         show_all = False
         if req.args.get('show') == 'all':
             show_all = True
-        req.hdf['config.show_all'] = show_all
+        data['show_all'] = show_all
 
-        configs = BuildConfig.select(self.env, include_inactive=show_all)
-        for idx, config in enumerate(configs):
-            prefix = 'configs.%d' % idx
+        configs = []
+        for config in BuildConfig.select(self.env, include_inactive=show_all):
             description = config.description
             if description:
                 description = wiki_to_html(description, self.env, req)
-            req.hdf[prefix] = {
+            config_data = {
                 'name': config.name, 'label': config.label or config.name,
                 'active': config.active, 'path': config.path,
                 'description': description,
                 'href': req.href.build(config.name),
+                'builds': []
             }
+            configs.append(config_data)
             if not config.active:
                 continue
 
@@ -176,69 +161,74 @@
                 if rev != prev_rev:
                     if prev_rev is None:
                         chgset = repos.get_changeset(rev)
-                        req.hdf[prefix + '.youngest_rev'] = {
+                        config_data['youngest_rev'] = {
                             'id': rev, 'href': req.href.changeset(rev),
                             'author': chgset.author or 'anonymous',
                             'date': format_datetime(chgset.date),
                             'message': wiki_to_oneliner(
-                                shorten_line(chgset.message), self.env, req=req
-                            )
+                                shorten_line(chgset.message), self.env, req=req)
                         }
                     else:
                         break
                     prev_rev = rev
                 if build:
-                    build_hdf = _build_to_hdf(self.env, req, build)
-                    build_hdf['platform'] = platform.name
-                    req.hdf[prefix + '.builds.%d' % platform.id] = build_hdf
+                    build_data = _get_build_data(self.env, req, build)
+                    build_data['platform'] = platform.name
+                    config_data['builds'].append(build_data)
                 else:
-                    req.hdf[prefix + '.builds.%d' % platform.id] = {
+                    config_data['builds'].append({
                         'platform': platform.name, 'status': 'pending'
-                    }
+                    })
 
-        req.hdf['page.mode'] = 'overview'
+        data['configs'] = configs
+        data['page_mode'] = 'overview'
         add_link(req, 'views', req.href.build(view='inprogress'),
                  'In Progress Builds')
+        add_ctxtnav(req, 'In Progress Builds',
+                    req.href.build(view='inprogress'))
+        return data
 
     def _render_inprogress(self, req):
-        req.hdf['title'] = 'In Progress Builds'
+        data = {'title': 'In Progress Builds',
+                'page_mode': 'view-inprogress'}
 
         db = self.env.get_db_cnx()
 
-        configs = BuildConfig.select(self.env, include_inactive=False)
-        for idx, config in enumerate(configs):
+        configs = []
+        for config in BuildConfig.select(self.env, include_inactive=False):
+            self.log.debug(config.name)
             if not config.active:
                 continue
 
             in_progress_builds = Build.select(self.env, config=config.name,
                                               status=Build.IN_PROGRESS, db=db)
 
-            # sort correctly by revision.
-            builds = list(in_progress_builds)
-            builds.sort(lambda x, y: int(y.rev) - int(x.rev))
-            prefix = 'configs.%d' % idx
-
             current_builds = 0
-            for idx2, build in enumerate(builds):
-                prefix2 = '%s.builds.%d' % (prefix, idx2)
+            builds = []
+            # sort correctly by revision.
+            for build in sorted(in_progress_builds,
+                                cmp=lambda x, y: int(y.rev) - int(x.rev)):
                 rev = build.rev
-                req.hdf[prefix2] = _build_to_hdf(self.env, req, build)
-                req.hdf[prefix2 + '.rev'] = rev
-                req.hdf[prefix2 + '.rev_href'] = req.href.changeset(rev)
+                build_data = _get_build_data(self.env, req, build)
+                build_data['rev'] = rev
+                build_data['rev_href'] = req.href.changeset(rev)
                 platform = TargetPlatform.fetch(self.env, build.platform)
-                req.hdf[prefix2 + '.platform'] = platform.name
+                build_data['platform'] = platform.name
+                build_data['steps'] = []
 
                 for step in BuildStep.select(self.env, build=build.id, db=db):
-                    req.hdf['%s.steps.%s' % (prefix2, step.name)] = {
-                         'description': step.description,
-                         'duration': datetime.fromtimestamp(step.stopped) - \
-                                     datetime.fromtimestamp(step.started),
-                         'failed': not step.successful,
-                         'errors': step.errors,
-                         'href': req.hdf[prefix2 + '.href'] + '#step_' + step.name
-                    }
+                    build_data['steps'].append({
+                        'name': step.name,
+                        'description': step.description,
+                        'duration': datetime.fromtimestamp(step.stopped) - \
+                                    datetime.fromtimestamp(step.started),
+                        'failed': not step.successful,
+                        'errors': step.errors,
+                        'href': build_data['href'] + '#step_' + step.name
+                    })
 
-                current_builds = current_builds + 1
+                builds.append(build_data)
+                current_builds += 1
 
             if current_builds == 0: 
                 continue
@@ -246,26 +236,29 @@
             description = config.description
             if description:
                 description = wiki_to_html(description, self.env, req)
-            req.hdf[prefix] = {
+            configs.append({
                 'name': config.name, 'label': config.label or config.name,
                 'active': config.active, 'path': config.path,
                 'description': description,
                 'href': req.href.build(config.name),
-            }
+                'builds': builds
+            })
 
-        req.hdf['page.mode'] = 'view_inprogress'
+        data['configs'] = configs
+        return data
 
     def _render_config(self, req, config_name):
         db = self.env.get_db_cnx()
 
         config = BuildConfig.fetch(self.env, config_name, db=db)
-        req.hdf['title'] = 'Build Configuration "%s"' \
-                           % config.label or config.name
+        data = {'title': 'Build Configuration "%s"' \
+                          % config.label or config.name,
+                'page_mode': 'view_config'}
         add_link(req, 'up', req.href.build(), 'Build Status')
         description = config.description
         if description:
             description = wiki_to_html(description, self.env, req)
-        req.hdf['config'] = {
+        data['config'] = {
             'name': config.name, 'label': config.label, 'path': config.path,
             'min_rev': config.min_rev,
             'min_rev_href': req.href.changeset(config.min_rev),
@@ -274,12 +267,12 @@
             'active': config.active, 'description': description,
             'browser_href': req.href.browser(config.path)
         }
-        req.hdf['page.mode'] = 'view_config'
 
         platforms = list(TargetPlatform.select(self.env, config=config_name,
                                                db=db))
-        req.hdf['config.platforms'] = [
-            {'name': platform.name, 'id': platform.id} for platform in platforms
+        data['config']['platforms'] = [
+            {'name': platform.name, 'id': platform.id}
+            for platform in platforms
         ]
 
         has_reports = False
@@ -294,14 +287,14 @@
                     chart_generators.append({
                         'href': req.href.build(config.name, 'chart/' + category) 
                     })
-            req.hdf['config.charts'] = chart_generators 
+            data['config']['charts'] = chart_generators 
             charts_license = self.config.get('bitten', 'charts_license')
             if charts_license:
-                req.hdf['config.charts_license'] = charts_license
+                data['config']['charts_license'] = charts_license
 
         page = max(1, int(req.args.get('page', 1)))
         more = False
-        req.hdf['page.number'] = page
+        data['page_number'] = page
 
         repos = self.env.get_repository(req.authname)
         if hasattr(repos, 'sync'):
@@ -309,28 +302,31 @@
 
         builds_per_page = 12 * len(platforms)
         idx = 0
+        builds = {}
         for platform, rev, build in collect_changes(repos, config):
             if idx >= page * builds_per_page:
                 more = True
                 break
             elif idx >= (page - 1) * builds_per_page:
-                prefix = 'config.builds.%s' % rev
-                req.hdf[prefix + '.href'] = req.href.changeset(rev)
+                builds.setdefault(rev, {})
+                builds[rev].setdefault('href', req.href.changeset(rev))
                 if build and build.status != Build.PENDING:
-                    build_hdf = _build_to_hdf(self.env, req, build)
-                    req.hdf['%s.%s' % (prefix, platform.id)] = build_hdf
+                    build_data = _get_build_data(self.env, req, build)
+                    build_data['steps'] = []
                     for step in BuildStep.select(self.env, build=build.id,
                                                  db=db):
-                        req.hdf['%s.%s.steps.%s' % (prefix, platform.id,
-                                                    step.name)] = {
+                        build_data['steps'].append({
+                            'name': step.name,
                             'description': step.description,
                             'duration': datetime.fromtimestamp(step.stopped) - \
                                         datetime.fromtimestamp(step.started),
                             'failed': not step.successful,
                             'errors': step.errors,
-                            'href': build_hdf['href'] + '#step_' + step.name,
-                        }
+                            'href': build_data['href'] + '#step_' + step.name
+                        })
+                    builds[rev][platform.id] = build_data
             idx += 1
+        data['config']['builds'] = builds
 
         if page > 1:
             if page == 2:
@@ -341,6 +337,8 @@
         if more:
             next_href = req.href.build(config.name, page=page + 1)
             add_link(req, 'next', next_href, 'Next Page')
+        prevnext_nav(req, 'Page')
+        return data
 
 
 class BuildController(Component):
@@ -370,7 +368,7 @@
             return True
 
     def process_request(self, req):
-        req.perm.assert_permission('BUILD_VIEW')
+        req.perm.require('BUILD_VIEW')
 
         db = self.env.get_db_cnx()
         build_id = int(req.args.get('id'))
@@ -386,11 +384,12 @@
                  'Build Configuration')
         status2title = {Build.SUCCESS: 'Success', Build.FAILURE: 'Failure',
                         Build.IN_PROGRESS: 'In Progress'}
-        req.hdf['title'] = 'Build %s - %s' % (build_id,
-                                              status2title[build.status])
-        req.hdf['page.mode'] = 'view_build'
+        data = {'title': 'Build %s - %s' % (build_id,
+                                            status2title[build.status]),
+                'page_mode': 'view_build',
+                'build': {}}
         config = BuildConfig.fetch(self.env, build.config, db=db)
-        req.hdf['build.config'] = {
+        data['build']['config'] = {
             'name': config.label,
             'href': req.href.build(config.name)
         }
@@ -404,7 +403,7 @@
             categories = summarizer.get_supported_categories()
             summarizers.update(dict([(cat, summarizer) for cat in categories]))
 
-        req.hdf['build'] = _build_to_hdf(self.env, req, build)
+        data['build'].update(_get_build_data(self.env, req, build))
         steps = []
         for step in BuildStep.select(self.env, build=build.id, db=db):
             steps.append({
@@ -416,20 +415,21 @@
                 'reports': self._render_reports(req, config, build, summarizers,
                                                 step)
             })
-        req.hdf['build.steps'] = steps
-        req.hdf['build.can_delete'] = req.perm.has_permission('BUILD_DELETE')
+        data['build']['steps'] = steps
+        data['build']['can_delete'] = ('BUILD_DELETE' in req.perm)
 
         repos = self.env.get_repository(req.authname)
         chgset = repos.get_changeset(build.rev)
-        req.hdf['build.chgset_author'] = chgset.author
+        data['build']['chgset_author'] = chgset.author
 
+        add_script(req, 'bitten/tabset.js')
         add_stylesheet(req, 'bitten/bitten.css')
-        return 'bitten_build.cs', None
+        return 'bitten_build.html', data, None
 
     # ITimelineEventProvider methods
 
     def get_timeline_filters(self, req):
-        if req.perm.has_permission('BUILD_VIEW'):
+        if 'BUILD_VIEW' in req.perm:
             yield ('build', 'Builds')
 
     def get_timeline_events(self, req, start, stop, filters):
@@ -465,9 +465,8 @@
                     errors += [(step.name, error) for error
                                in step.errors]
 
-            title = Markup('Build of <em>%s [%s]</em> on %s %s') % (
-                label, rev, platform, _status_label[status]
-            )
+            title = tag('Build of ', tag.em('%s [%s]' % (label, rev)),
+                        ' on %s %s' % (platform, _status_label[status]))
             message = ''
             if req.args.get('format') == 'rss':
                 href = req.abs_href.build(config, id)
@@ -536,12 +535,12 @@
         for report in Report.select(self.env, build=build.id, step=step.name):
             summarizer = summarizers.get(report.category)
             if summarizer:
-                summary = summarizer.render_summary(req, config, build, step,
-                                                    report.category)
+                tmpl, data = summarizer.render_summary(req, config, build,
+                                                        step, report.category)
             else:
-                summary = None
+                tmpl = data = None
             reports.append({'category': report.category,
-                            'summary': Markup(summary)})
+                            'template': tmpl, 'data': data})
         return reports
 
 
@@ -565,13 +564,13 @@
 
         for generator in self.generators:
             if category in generator.get_supported_categories():
-                template = generator.generate_chart_data(req, config,
-                                                         category)
+                tmpl, data = generator.generate_chart_data(req, config,
+                                                           category)
                 break
         else:
             raise TracError('Unknown report category "%s"' % category)
 
-        return template, 'text/xml'
+        return tmpl, data, 'text/xml'
 
 
 class SourceFileLinkFormatter(Component):
@@ -609,7 +608,7 @@
             link = href(config.path, filepath)
             if m.group('line'):
                 link += '#L' + m.group('line')[1:]
-            return Markup(html.A(m.group(0), href=link))
+            return Markup(tag.a(m.group(0), href=link))
 
         def _formatter(step, type, level, message):
             buf = []
--- a/setup.py
+++ b/setup.py
@@ -38,7 +38,7 @@
     package_data = {
         'bitten': ['htdocs/*.*',
                    'htdocs/charts_library/*.swf',
-                   'templates/*.cs']
+                   'templates/*.html']
     },
     test_suite = 'bitten.tests.suite',
     entry_points = {
@@ -62,6 +62,7 @@
             NS + 'c#configure = bitten.build.ctools:configure',
             NS + 'c#autoreconf = bitten.build.ctools:autoreconf',
             NS + 'c#cppunit = bitten.build.ctools:cppunit',
+            NS + 'c#cunit = bitten.build.ctools:cunit',
             NS + 'c#gcov = bitten.build.ctools:gcov',
             NS + 'c#make = bitten.build.ctools:make',
             NS + 'java#ant = bitten.build.javatools:ant',
deleted file mode 100644
--- a/trac-0.11/COPYING
+++ /dev/null
@@ -1,36 +0,0 @@
-Copyright (C) 2007 Edgewall Software
-Copyright (C) 2005-2007 Christopher Lenz
-All rights reserved.
-
-Redistribution and use in source and binary forms, with or without
-modification, are permitted provided that the following conditions
-are met:
-
- 1. Redistributions of source code must retain the above copyright
-    notice, this list of conditions and the following disclaimer.
- 2. Redistributions in binary form must reproduce the above copyright
-    notice, this list of conditions and the following disclaimer in
-    the documentation and/or other materials provided with the
-    distribution.
- 3. The name of the author may not be used to endorse or promote
-    products derived from this software without specific prior
-    written permission.
-
-THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS
-OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
-WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
-ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY
-DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
-DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE
-GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
-INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER
-IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
-OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN
-IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
-
-====================================================================
-
-This software includes a copy of XML/SWF charts, which is
-Copyright (C) 2004-2005 maani.us
-See http://www.maani.us/xml_charts/index.php?menu=License for more
-information.
deleted file mode 100644
--- a/trac-0.11/ChangeLog
+++ /dev/null
@@ -1,71 +0,0 @@
-Version 0.6
-(?, from 0.6.x branch)
-http://svn.edgewall.org/repos/bitten/tags/0.6.0
-
- * Switch to using HTTP for communication between the build master and build
-   slaves. This means the `build-master` executable is no longer needed or
-   installed, the build simply runs in the scope of the Trac site.
- * Build recipes now need to include instructions for performing the checkout 
-   from the version control repository. The slave no longer receives a snapshot
-   archive of the code, but performs the checkout itself based on the 
-   instructions in the build recipe.
- * Many fixes for compatibility with more recent versions of Trac.
-
-
-Version 0.5.3
-(18 April 2006, from 0.5.x branch)
-http://svn.edgewall.org/repos/bitten/tags/0.5.3
-
- * Fix double-escaping of report summaries.
- * Fix build master error when build log contains no messages.
-
-
-Version 0.5.2
-(17 January 2006, from 0.5.x branch)
-http://svn.edgewall.org/repos/bitten/tags/0.5.2
-
- * Fixes the main navigation tab that was broken in 0.5.1.
-
-
-Version 0.5.1
-(10 January 2006, from 0.5.x branch)
-http://svn.edgewall.org/repos/bitten/tags/0.5.1
-
- * Fixes compatibility with Trac 0.9.3 release, as well as the current trunk.
-   This also means that Bitten now longer works with versions of Trac earlier
-   than 0.9.3.
- * Improves PostgreSQL compatibility.
- * Fixes encoding of non-ASCII characters in command output.
- * Fix for missing log output when using <java:ant> on Windows.
-
-
-Version 0.5
-(6 October 2005, from 0.5.x branch)
-http://svn.edgewall.org/repos/bitten/tags/0.5
-
- * BDB XML is no longer being used for report storage. Instead,
-   collected metrics data is stored in the Trac database.
- * Snapshot archives created by the master are checked for integrity
-   prior to their transmission to the slaves.
- * Improvements to the build status presentation in Trac.
- * Changes to the build recipe format. See the documentation on the web
-   site for details.
- * New recipe commands: <sh:pipe>, <c:configure>, <c:cppunit>,
-   <java:ant>, <java:junit>, and <x:transform>. Various improvements to
-   the existing commands.
- * Recipe commands and command attributes in recipes can now reference
-   slave configuration values.
- * The names of the master and slaves scripts have changed: `bittend`
-   is now `bitten-master`, `bitten` is now `bitten-slave`.
- * The build master can now handle multiple Trac environments.
- * The build slave now by default removes any working directories when
-   done.
- * Build configurations can now be completely deleted.
- * Build configurations can now have a minimum and maximum revision
-   specified. Any revisions outside that range will not be built.
- * The build configuration editor now validates the supplied values.
- * Fix management of target platforms when running under mod_python.
- * Improved performance of the build log formatter that is responsible
-   for linking file references in build logs to the repository browser.
- * Add paging to the build configuration view.
- * Fix compatibility with PySQLite2.
deleted file mode 100644
--- a/trac-0.11/MANIFEST.in
+++ /dev/null
@@ -1,2 +0,0 @@
-include doc/api/*.*
-include doc/*.html
deleted file mode 100644
--- a/trac-0.11/README.txt
+++ /dev/null
@@ -1,141 +0,0 @@
-About Bitten
-============
-
-Bitten is a simple distributed continuous integration system that not only
-coordinates builds across multiple machines, but also collects software
-metrics generated by builds, to enable feedback and reporting about
-the progress of a software project.
-
-The Bitten software consists of three separate parts:
- * The build slave, which executes builds on behalf of a local or remote
-   build master
- * The build master, which orchestrates builds for a project across all
-   connected slaves, and stores the build status and results to the
-   database
- * The web interface, which is implemented as an add-on to Trac
-   (http://trac.edgewall.com/) and provides a build management interface
-   as well as presentation of build results.
-
-Both the build master and the web interface depend on Trac 0.10, and need
-to be installed on the same machine, together with the Subversion
-repository. The build slave only requires Python (>= 2.3), setuptools
-(>= 0.6a2),  as well as any tools required by the build process itself. A
-build slave may be run on any machine that can connect to the server
-running the Bitten build master.
-
-
-Installation
-------------
-
-Bitten is written in Python, so make sure that you have Python installed.
-You'll need Python 2.3 or later. Also, make sure that setuptools
-(http://peak.telecommunity.com/DevCenter/setuptools), version 0.6a2 or later,
-is installed.
-
-If that's taken care of, you just need to download and unpack the Bitten
-distribution, and execute the command:
-
-  $ python setup.py install
-
-from the top of the directory where you unpacked (or checked out) the Bitten
-code. Note that you may need administrator/root privileges for this step, as
-it will by default attempt to install Bitten to the Python site-packages
-directory on your system.
-
-It's also a good idea to run the unit tests at this point, to make sure that
-the code works as expected on your platform:
-
-  $ python setup.py test
-
-
-What's left to do now depends on whether you want to use the build master and
-web interface, or just the build slave. In the latter case, you're already
-done. You might need to install software that the build of your project
-requires, but the Bitten build slave itself doesn't require anything extra.
-
-For the build master and web interface, you'll need to install Trac 0.10 or
-later. Please refer to the Trac documentation for information on how it is
-installed.
-
-
-Build Master Configuration
---------------------------
-
-Once both Bitten and Trac are installed and working, you'll have to introduce
-Bitten to your Trac project environment. If you don't have a  Trac project
-set up yet, you'll need to do so in order to use Bitten.
-
-If you already have a Trac project environment, the Bitten plugin needs to be
-explicitly enabled in the Trac configuration. This is done by adding it to the
-[components] section in /path/to/projenv/conf/trac.ini:
-
-  [components]
-  bitten.* = enabled
-
-The Trac web interface should now inform you with an error message that the
-environment needs to be upgraded. To do this, run:
-
-  $ trac-admin /path/to/projenv upgrade
-
-This will create the database tables and directories that Bitten requires.
-You probably also want to grant permissions to someone (such as yourself)
-to manage build configurations, and allow anonymous users to view the
-status and results of builds:
-
-  $ trac-admin /path/to/projenv permission add anonymous BUILD_EXEC
-  $ trac-admin /path/to/projenv permission add anonymous BUILD_VIEW
-  $ trac-admin /path/to/projenv permission add [yourname] BUILD_ADMIN
-
-You should now see an additional tab labeled "Build Status" in the Trac
-navigation bar. This link will take you to the list of build configurations,
-which at this point is of course empty. If you've set up permissions
-correctly as described previously, you should see a button for adding new
-build configurations. Click that button and fill out the form. Also, add
-at least one target platform after saving the configuration. Last but not
-least, you'll have to "activate" the build configuration.
-
-
-Running the Build Master
-------------------------
-
-At this point, you're ready to start the Bitten build master. The
-installation of Bitten should have put a `bitten-master` executable on your
-path. If the script is not on your path, look for it in the `bin` or
-`scripts` subdirectory of your Python installation.
-
-To find out about the options and arguments of the master, execute it with
-the `--help` option as follows:
-
-  $ bitten-master --help
-
-Most commonly, you'll want to specify the log level and log file, as well as
-the path to the Trac environment:
-
-  $ bitten-master --verbose --log=/var/log/bittend /var/trac/myproject
-
-
-Running the Build Slave
------------------------
-
-The build slave can be run on any machine that can connect to the machine
-on which the build master is running. The installation of Bitten should have put
-a `bitten-slave` executable on your path. If the script is not on your path,
-look for it in the `bin` or `scripts` subdirectory of your Python installation.
-
-To get a list of options for the build slave, execute it with the `--help`
-option:
-
-  $ bitten-slave --help
-
-To run the build slave against a Bitten-enabled Trac site installed at 
-http://myproject.example.org/trac, you'd run:
-
-  $ bitten-slave http://myproject.example.org/trac/builds
-
-
-More Information
-----------------
-
-For further documentation, please see the Bitten website at:
-
-  <http://bitten.cmlenz.net/>
deleted file mode 100644
--- a/trac-0.11/bitten/__init__.py
+++ /dev/null
@@ -1,15 +0,0 @@
-# -*- coding: utf-8 -*-
-#
-# Copyright (C) 2007 Edgewall Software
-# Copyright (C) 2005-2007 Christopher Lenz <cmlenz@gmx.de>
-# 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.
-
-__docformat__ = 'restructuredtext en'
-try:
-    __version__ = __import__('pkg_resources').get_distribution('Bitten').version
-except ImportError:
-    pass
deleted file mode 100644
--- a/trac-0.11/bitten/admin.py
+++ /dev/null
@@ -1,343 +0,0 @@
-# -*- coding: utf-8 -*-
-#
-# 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.
-
-"""Implementation of the web administration interface."""
-
-from pkg_resources import require, DistributionNotFound
-import re
-
-from trac.core import *
-from trac.admin import IAdminPanelProvider
-from trac.web.chrome import add_stylesheet
-
-from bitten.model import BuildConfig, TargetPlatform
-from bitten.recipe import Recipe, InvalidRecipeError
-from bitten.util import xmlio
-
-
-class BuildMasterAdminPageProvider(Component):
-    """Web administration panel for configuring the build master."""
-
-    implements(IAdminPanelProvider)
-
-    # IAdminPanelProvider methods
-
-    def get_admin_panels(self, req):
-        if req.perm.has_permission('BUILD_ADMIN'):
-            yield ('bitten', 'Builds', 'master', 'Master Settings')
-
-    def render_admin_panel(self, req, cat, page, path_info):
-        from bitten.master import BuildMaster
-        master = BuildMaster(self.env)
-
-        if req.method == 'POST':
-            self._save_config_changes(req, master)
-            req.redirect(req.abs_href.admin(cat, page))
-
-        data = {'master': master}
-        add_stylesheet(req, 'bitten/admin.css')
-        return 'bitten_admin_master.html', data
-
-    # Internal methods
-
-    def _save_config_changes(self, req, master):
-        changed = False
-
-        build_all = 'build_all' in req.args
-        if build_all != master.build_all:
-            self.config['bitten'].set('build_all',
-                                      build_all and 'yes' or 'no')
-            changed = True
-
-        adjust_timestamps = 'adjust_timestamps' in req.args
-        if adjust_timestamps != master.adjust_timestamps:
-            self.config['bitten'].set('adjust_timestamps',
-                                      adjust_timestamps and 'yes' or 'no')
-            changed = True
-
-        stabilize_wait = int(req.args.get('stabilize_wait', 0))
-        if stabilize_wait != master.stabilize_wait:
-            self.config['bitten'].set('stabilize_wait', str(stabilize_wait))
-            changed = True
-
-        slave_timeout = int(req.args.get('slave_timeout', 0))
-        if slave_timeout != master.slave_timeout:
-            self.config['bitten'].set('slave_timeout', str(slave_timeout))
-            changed = True
-
-        if changed:
-            self.config.save()
-
-        return master
-
-
-class BuildConfigurationsAdminPageProvider(Component):
-    """Web administration panel for configuring the build master."""
-
-    implements(IAdminPanelProvider)
-
-    # IAdminPanelProvider methods
-
-    def get_admin_panels(self, req):
-        if req.perm.has_permission('BUILD_MODIFY'):
-            yield ('bitten', 'Builds', 'configs', 'Configurations')
-
-    def render_admin_panel(self, req, cat, page, path_info):
-        data = {}
-
-        # Analyze url
-        try:
-            config_name, platform_id = path_info.split('/', 1)
-        except:
-            config_name = path_info
-            platform_id = None
-
-        if config_name: # Existing build config
-            if platform_id or (
-                    # Editing or creating one of the config's target platforms
-                    req.method == 'POST' and 'new' in req.args):
-
-                if platform_id: # Editing target platform
-                    platform_id = int(platform_id)
-                    platform = TargetPlatform.fetch(self.env, platform_id)
-
-                    if req.method == 'POST':
-                        if 'cancel' in req.args or \
-                                self._update_platform(req, platform):
-                            req.redirect(req.abs_href.admin(cat, page,
-                                                            config_name))
-                else: # creating target platform
-                    if req.method == 'POST':
-                        if 'add' in req.args:
-                            self._create_platform(req, config_name)
-                            req.redirect(req.abs_href.admin(cat, page,
-                                                            config_name))
-                        elif 'cancel' in req.args:
-                            req.redirect(req.abs_href.admin(cat, page,
-                                                            config_name))
-
-                    platform = TargetPlatform(self.env, config=config_name)
-
-                # Set up template variables
-                data['platform'] = {
-                    'id': platform.id, 'name': platform.name,
-                    'exists': platform.exists,
-                    'rules': [
-                        {'property': propname, 'pattern': pattern}
-                        for propname, pattern in platform.rules
-                    ] or [('', '')]
-                }
-
-            else: # Editing existing build config itself
-                config = BuildConfig.fetch(self.env, config_name)
-                platforms = list(TargetPlatform.select(self.env,
-                                                       config=config.name))
-
-                if req.method == 'POST':
-                    if 'add' in req.args: # Add target platform
-                        platform = self._create_platform(req, config)
-                        req.redirect(req.abs_href.admin(cat, page, config.name))
-
-                    elif 'remove' in req.args: # Remove selected platforms
-                        self._remove_platforms(req)
-                        req.redirect(req.abs_href.admin(cat, page, config.name))
-
-                    elif 'save' in req.args: # Save this build config
-                        self._update_config(req, config)
-
-                    req.redirect(req.abs_href.admin(cat, page))
-
-                # Prepare template variables
-                data['config'] = {
-                    'name': config.name, 'label': config.label or config.name,
-                    'active': config.active, 'path': config.path,
-                    'min_rev': config.min_rev, 'max_rev': config.max_rev,
-                    'description': config.description,
-                    'recipe': config.recipe,
-                    'platforms': [{
-                        'name': platform.name,
-                        'id': platform.id,
-                        'href': req.href.admin('bitten', 'configs', config.name,
-                                               platform.id),
-                        'rules': [{'property': propname, 'pattern': pattern}
-                                   for propname, pattern in platform.rules]
-                    } for platform in platforms]
-                }
-
-        else: # At the top level build config list
-            if req.method == 'POST':
-                if 'add' in req.args: # Add build config
-                    config = self._create_config(req)
-                    req.redirect(req.abs_href.admin(cat, page, config.name))
-
-                elif 'remove' in req.args: # Remove selected build configs
-                    self._remove_configs(req)
-
-                elif 'apply' in req.args: # Update active state of configs
-                    self._activate_configs(req)
-                req.redirect(req.abs_href.admin(cat, page))
-
-            # Prepare template variables
-            configs = []
-            for config in BuildConfig.select(self.env, include_inactive=True):
-                configs.append({
-                    'name': config.name, 'label': config.label or config.name,
-                    'active': config.active, 'path': config.path,
-                    'min_rev': config.min_rev, 'max_rev': config.max_rev,
-                    'href': req.href.admin('bitten', 'configs', config.name),
-                })
-            data['configs'] = configs
-
-        add_stylesheet(req, 'bitten/admin.css')
-        return 'bitten_admin_configs.html', data
-
-    # Internal methods
-
-    def _activate_configs(self, req):
-        req.perm.assert_permission('BUILD_MODIFY')
-
-        active = req.args.get('active') or []
-        active = isinstance(active, list) and active or [active]
-
-        db = self.env.get_db_cnx()
-        for config in list(BuildConfig.select(self.env, db=db,
-                                              include_inactive=True)):
-            config.active = config.name in active
-            config.update(db=db)
-        db.commit()
-
-    def _create_config(self, req):
-        req.perm.assert_permission('BUILD_CREATE')
-
-        config = BuildConfig(self.env)
-        self._update_config(req, config)
-        return config
-
-    def _remove_configs(self, req):
-        req.perm.assert_permission('BUILD_DELETE')
-
-        sel = req.args.get('sel')
-        if not sel:
-            raise TracError('No configuration selected')
-        sel = isinstance(sel, list) and sel or [sel]
-
-        db = self.env.get_db_cnx()
-        for name in sel:
-            config = BuildConfig.fetch(self.env, name, db=db)
-            if not config:
-                raise TracError('Configuration %r not found' % name)
-            config.delete(db=db)
-        db.commit()
-
-    def _update_config(self, req, config):
-        req.perm.assert_permission('BUILD_MODIFY')
-
-        name = req.args.get('name')
-        if not name:
-            raise TracError('Missing required field "name"', 'Missing Field')
-        if not re.match(r'^[\w.-]+$', name):
-            raise TracError('The field "name" may only contain letters, '
-                            'digits, periods, or dashes.', 'Invalid Field')
-
-        path = req.args.get('path', '')
-        repos = self.env.get_repository(req.authname)
-        max_rev = req.args.get('max_rev') or None
-        try:
-            node = repos.get_node(path, max_rev)
-            assert node.isdir, '%s is not a directory' % node.path
-        except (AssertionError, TracError), e:
-            raise TracError(unicode(e), 'Invalid Repository Path')
-        if req.args.get('min_rev'):
-            try:
-                repos.get_node(path, req.args.get('min_rev'))
-            except TracError, e:
-                raise TracError(unicode(e), 'Invalid Oldest Revision')
-
-        recipe_xml = req.args.get('recipe', '')
-        if recipe_xml:
-            try:
-                Recipe(xmlio.parse(recipe_xml)).validate()
-            except xmlio.ParseError, e:
-                raise TracError('Failure parsing recipe: %s' % e,
-                                'Invalid Recipe')
-            except InvalidRecipeError, e:
-                raise TracError(unicode(e), 'Invalid Recipe')
-
-        config.name = name
-        config.path = repos.normalize_path(path)
-        config.recipe = recipe_xml
-        config.min_rev = req.args.get('min_rev')
-        config.max_rev = req.args.get('max_rev')
-        config.label = req.args.get('label', config.name)
-        config.description = req.args.get('description', '')
-
-        if config.exists:
-            config.update()
-        else:
-            config.insert()
-
-    def _create_platform(self, req, config_name):
-        req.perm.assert_permission('BUILD_MODIFY')
-
-        name = req.args.get('name')
-        if not name:
-            raise TracError('Missing required field "name"', 'Missing field')
-
-        platform = TargetPlatform(self.env, config=config_name, name=name)
-        self._update_platform(req, platform)
-        return platform
-
-    def _remove_platforms(self, req):
-        req.perm.assert_permission('BUILD_MODIFY')
-
-        sel = req.args.get('sel')
-        if not sel:
-            raise TracError('No platform selected')
-        sel = isinstance(sel, list) and sel or [sel]
-
-        db = self.env.get_db_cnx()
-        for platform_id in sel:
-            platform = TargetPlatform.fetch(self.env, platform_id, db=db)
-            if not platform:
-                raise TracError('Target platform %r not found' % platform_id)
-            platform.delete(db=db)
-        db.commit()
-
-    def _update_platform(self, req, platform):
-        platform.name = req.args.get('name')
-
-        properties = [int(key[9:]) for key in req.args.keys()
-                      if key.startswith('property_')]
-        properties.sort()
-        patterns = [int(key[8:]) for key in req.args.keys()
-                    if key.startswith('pattern_')]
-        patterns.sort()
-        platform.rules = [(req.args.get('property_%d' % property),
-                           req.args.get('pattern_%d' % pattern))
-                          for property, pattern in zip(properties, patterns)
-                          if req.args.get('property_%d' % property)]
-
-        if platform.exists:
-            platform.update()
-        else:
-            platform.insert()
-
-        add_rules = [int(key[9:]) for key in req.args.keys()
-                     if key.startswith('add_rule_')]
-        if add_rules:
-            platform.rules.insert(add_rules[0] + 1, ('', ''))
-            return False
-        rm_rules = [int(key[8:]) for key in req.args.keys()
-                    if key.startswith('rm_rule_')]
-        if rm_rules:
-            if rm_rules[0] < len(platform.rules):
-                del platform.rules[rm_rules[0]]
-            return False
-
-        return True
deleted file mode 100644
--- a/trac-0.11/bitten/api.py
+++ /dev/null
@@ -1,120 +0,0 @@
-# -*- 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.
-
-"""Interfaces of extension points provided by the Bitten Trac plugin."""
-
-from trac.core import *
-
-__all__ = ['IBuildListener', 'ILogFormatter', 'IReportChartGenerator',
-           'IReportSummarizer']
-__docformat__ = 'restructuredtext en'
-
-
-class IBuildListener(Interface):
-    """Extension point interface for components that need to be notified of
-    build events.
-    
-    Note that these will be notified in the process running the build master,
-    not the web interface.
-    """
-
-    def build_started(build):
-        """Called when a build slave has accepted a build initiation.
-        
-        :param build: the build that was started
-        :type build: `Build`
-        """
-
-    def build_aborted(build):
-        """Called when a build slave cancels a build or disconnects.
-        
-        :param build: the build that was aborted
-        :type build: `Build`
-        """
-
-    def build_completed(build):
-        """Called when a build slave has completed a build, regardless of the
-        outcome.
-        
-        :param build: the build that was aborted
-        :type build: `Build`
-        """
-
-
-class ILogFormatter(Interface):
-    """Extension point interface for components that format build log
-    messages."""
-
-    def get_formatter(req, build):
-        """Return a function that gets called for every log message.
-        
-        The function must take four positional arguments, ``step``,
-        ``generator``, ``level`` and ``message``, and return the formatted
-        message as a string.
-
-        :param req: the request object
-        :param build: the build to which the logs belong that should be
-                      formatted
-        :type build: `Build`
-        :return: the formatted log message
-        :rtype: `basestring`
-        """
-
-
-class IReportSummarizer(Interface):
-    """Extension point interface for components that render a summary of reports
-    of some kind."""
-
-    def get_supported_categories():
-        """Return a list of strings identifying the types of reports this 
-        component supports.
-        """
-
-    def render_summary(req, config, build, step, category):
-        """Render a summary for the given report.
-        
-        This function should return a tuple of the form `(template, data)`,
-        where `template` is the name of the template to use and `data` is the
-        data to be passed to the template.
-        
-        :param req: the request object
-        :param config: the build configuration
-        :type config: `BuildConfig`
-        :param build: the build
-        :type build: `Build`
-        :param step: the build step
-        :type step: `BuildStep`
-        :param category: the category of the report that should be summarized
-        :type category: `basestring`
-        """
-
-
-class IReportChartGenerator(Interface):
-    """Extension point interface for components that generate a chart for a
-    set of reports."""
-
-    def get_supported_categories():
-        """Return a list of strings identifying the types of reports this 
-        component supports.
-        """
-
-    def generate_chart_data(req, config, category):
-        """Generate the data for a report chart.
-        
-        This function should return a tuple of the form `(template, data)`,
-        where `template` is the name of the template to use and `data` is the
-        data to be passed to the template.
-        
-        :param req: the request object
-        :param config: the build configuration
-        :type config: `BuildConfig`
-        :param category: the category of reports to include in the chart
-        :type category: `basestring`
-        """
deleted file mode 100644
--- a/trac-0.11/bitten/build/__init__.py
+++ /dev/null
@@ -1,13 +0,0 @@
-# -*- 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.
-
-from bitten.build.api import *
-
-__docformat__ = 'restructuredtext en'
deleted file mode 100644
--- a/trac-0.11/bitten/build/api.py
+++ /dev/null
@@ -1,290 +0,0 @@
-# -*- 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.
-
-"""Functions and classes used to simplify the implementation recipe commands."""
-
-import logging
-import fnmatch
-import os
-import shlex
-import time
-
-log = logging.getLogger('bitten.build.api')
-
-__docformat__ = 'restructuredtext en'
-
-
-class BuildError(Exception):
-    """Exception raised when a build fails."""
-
-
-class TimeoutError(Exception):
-    """Exception raised when the execution of a command times out."""
-
-
-def _combine(*iterables):
-    iterables = [iter(iterable) for iterable in iterables]
-    size = len(iterables)
-    while True:
-        to_yield = [None] * size
-        for idx, iterable in enumerate(iterables):
-            if iterable is None:
-                continue
-            try:
-                to_yield[idx] = iterable.next()
-            except StopIteration:
-                iterables[idx] = None
-        if not [iterable for iterable in iterables if iterable is not None]:
-            break
-        yield tuple(to_yield)
-
-
-class CommandLine(object):
-    """Simple helper for executing subprocesses."""
-
-    def __init__(self, executable, args, input=None, cwd=None):
-        """Initialize the CommandLine object.
-        
-        :param executable: the name of the program to execute
-        :param args: a list of arguments to pass to the executable
-        :param input: string or file-like object containing any input data for
-                      the program
-        :param cwd: the working directory to change to before executing the
-                    command
-        """
-        self.executable = executable
-        self.arguments = [str(arg) for arg in args]
-        self.input = input
-        self.cwd = cwd
-        if self.cwd:
-            assert os.path.isdir(self.cwd)
-        self.returncode = None
-
-    if os.name == 'nt': # windows
-
-        def execute(self, timeout=None):
-            """Execute the command, and return a generator for iterating over
-            the output written to the standard output and error streams.
-            
-            :param timeout: number of seconds before the external process
-                            should be aborted (not supported on Windows)
-            """
-            args = [self.executable] + self.arguments
-            for idx, arg in enumerate(args):
-                if arg.find(' ') >= 0:
-                    args[idx] = '"%s"' % arg
-            log.debug('Executing %s', args)
-
-            if self.cwd:
-                old_cwd = os.getcwd()
-                os.chdir(self.cwd)
-
-            import tempfile
-            in_name = None
-            if self.input:
-                if isinstance(self.input, basestring):
-                    in_file, in_name = tempfile.mkstemp(prefix='bitten_',
-                                                        suffix='.pipe')
-                    os.write(in_file, self.input)
-                    os.close(in_file)
-                    in_redirect = '< "%s" ' % in_name
-                else:
-                    in_redirect = '< "%s" ' % self.input.name
-            else:
-                in_redirect = ''
-
-            out_file, out_name = tempfile.mkstemp(prefix='bitten_',
-                                                  suffix='.pipe')
-            os.close(out_file)
-            err_file, err_name = tempfile.mkstemp(prefix='bitten_',
-                                                  suffix='.pipe')
-            os.close(err_file)
-
-            try:
-                cmd = '( %s ) > "%s" %s 2> "%s"' % (' '.join(args), out_name,
-                                                    in_redirect, err_name)
-                self.returncode = os.system(cmd)
-                log.debug('Exited with code %s', self.returncode)
-
-                out_file = file(out_name, 'r')
-                err_file = file(err_name, 'r')
-                out_lines = out_file.readlines()
-                err_lines = err_file.readlines()
-                out_file.close()
-                err_file.close()
-            finally:
-                if in_name:
-                    os.unlink(in_name)
-                if out_name:
-                    os.unlink(out_name)
-                if err_name:
-                    os.unlink(err_name)
-                if self.cwd:
-                    os.chdir(old_cwd)
-
-            for out_line, err_line in _combine(out_lines, err_lines):
-                yield out_line and out_line.rstrip().replace('\x00', ''), \
-                      err_line and err_line.rstrip().replace('\x00', '')
-
-    else: # posix
-
-        def execute(self, timeout=None):
-            """Execute the command, and return a generator for iterating over
-            the output written to the standard output and error streams.
-            
-            :param timeout: number of seconds before the external process
-                            should be aborted (not supported on Windows)
-            """
-            import popen2, select
-            if self.cwd:
-                old_cwd = os.getcwd()
-                os.chdir(self.cwd)
-
-            log.debug('Executing %s', [self.executable] + self.arguments)
-            pipe = popen2.Popen3([self.executable] + self.arguments,
-                                 capturestderr=True)
-            if self.input:
-                if isinstance(self.input, basestring):
-                    in_data = self.input
-                else:
-                    in_data = self.input.read()
-            else:
-                pipe.tochild.close()
-                in_data = ''
-
-            out_data, err_data = [], []
-            in_eof = out_eof = err_eof = False
-            if not in_data:
-                in_eof = True
-            while not out_eof or not err_eof:
-                readable = [pipe.fromchild] * (not out_eof) + \
-                           [pipe.childerr] * (not err_eof)
-                writable = [pipe.tochild] * (not in_eof)
-                ready = select.select(readable, writable, [], timeout)
-                if not (ready[0] or ready[1]):
-                    raise TimeoutError('Command %s timed out' % self.executable)
-                if pipe.tochild in ready[1]:
-                    sent = os.write(pipe.tochild.fileno(), in_data)
-                    in_data = in_data[sent:]
-                    if not in_data:
-                        pipe.tochild.close()
-                        in_eof = True
-                if pipe.fromchild in ready[0]:
-                    data = os.read(pipe.fromchild.fileno(), 1024)
-                    if data:
-                        out_data.append(data)
-                    else:
-                        out_eof = True
-                if pipe.childerr in ready[0]:
-                    data = os.read(pipe.childerr.fileno(), 1024)
-                    if data:
-                        err_data.append(data)
-                    else:
-                        err_eof = True
-                out_lines = self._extract_lines(out_data)
-                err_lines = self._extract_lines(err_data)
-                for out_line, err_line in _combine(out_lines, err_lines):
-                    yield out_line, err_line
-                time.sleep(.1)
-            self.returncode = pipe.wait()
-            log.debug('%s exited with code %s', self.executable,
-                      self.returncode)
-
-            if self.cwd:
-                os.chdir(old_cwd)
-
-    def _extract_lines(self, data):
-        extracted = []
-        def _endswith_linesep(string):
-            for linesep in ('\n', '\r\n', '\r'):
-                if string.endswith(linesep):
-                    return True
-        buf = ''.join(data)
-        lines = buf.splitlines(True)
-        if len(lines) > 1:
-            extracted += lines[:-1]
-            if _endswith_linesep(lines[-1]):
-                extracted.append(lines[-1])
-                buf = ''
-            else:
-                buf = lines[-1]
-        elif _endswith_linesep(buf):
-            extracted.append(buf)
-            buf = ''
-        data[:] = [buf] * bool(buf)
-
-        return [line.rstrip() for line in extracted]
-
-
-class FileSet(object):
-    """Utility class for collecting a list of files in a directory that match
-    given name/path patterns."""
-
-    DEFAULT_EXCLUDES = ['CVS/*', '*/CVS/*', '.svn/*', '*/.svn/*',
-                        '.DS_Store', 'Thumbs.db']
-
-    def __init__(self, basedir, include=None, exclude=None):
-        """Create a file set.
-        
-        :param basedir: the base directory for all files in the set
-        :param include: a list of patterns that define which files should be
-                        included in the set
-        :param exclude: a list of patterns that define which files should be
-                        excluded from the set
-        """
-        self.files = []
-        self.basedir = basedir
-
-        self.include = []
-        if include is not None:
-            self.include = shlex.split(include)
-
-        self.exclude = self.DEFAULT_EXCLUDES[:]
-        if exclude is not None:
-            self.exclude += shlex.split(exclude)
-
-        for dirpath, dirnames, filenames in os.walk(self.basedir):
-            dirpath = dirpath[len(self.basedir) + 1:]
-
-            for filename in filenames:
-                filepath = nfilepath = os.path.join(dirpath, filename)
-                if os.sep != '/':
-                    nfilepath = nfilepath.replace(os.sep, '/')
-
-                if self.include:
-                    included = False
-                    for pattern in self.include:
-                        if fnmatch.fnmatchcase(nfilepath, pattern) or \
-                           fnmatch.fnmatchcase(filename, pattern):
-                            included = True
-                            break
-                    if not included:
-                        continue
-
-                excluded = False
-                for pattern in self.exclude:
-                    if fnmatch.fnmatchcase(nfilepath, pattern) or \
-                       fnmatch.fnmatchcase(filename, pattern):
-                        excluded = True
-                        break
-                if not excluded:
-                    self.files.append(filepath)
-
-    def __iter__(self):
-        """Iterate over the names of all files in the set."""
-        for filename in self.files:
-            yield filename
-
-    def __contains__(self, filename):
-        """Return whether the given file name is in the set.
-        
-        :param filename: the name of the file to check
-        """
-        return filename in self.files
deleted file mode 100644
--- a/trac-0.11/bitten/build/config.py
+++ /dev/null
@@ -1,178 +0,0 @@
-# -*- 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.
-
-"""Support for build slave configuration."""
-
-from ConfigParser import SafeConfigParser
-import logging
-import os
-import platform
-import re
-
-log = logging.getLogger('bitten.config')
-
-__docformat__ = 'restructuredtext en'
-
-
-class Configuration(object):
-    """Encapsulates the configuration of a build machine.
-    
-    Configuration values can be provided through a configuration file (in INI
-    format) or through command-line parameters (properties). In addition to
-    explicitly defined properties, this class automatically collects platform
-    information and stores them as properties. These defaults can be
-    overridden (useful for cross-compilation).
-    """
-    # TODO: document mapping from config file to property names
-
-    def __init__(self, filename=None, properties=None):
-        """Create the configuration object.
-        
-        :param filename: the path to the configuration file, if any
-        :param properties: a dictionary of the configuration properties
-                           provided on the command-line
-        """
-        self.properties = {}
-        self.packages = {}
-        parser = SafeConfigParser()
-        if filename:
-            parser.read(filename)
-        self._merge_sysinfo(parser, properties)
-        self._merge_packages(parser, properties)
-
-    def _merge_sysinfo(self, parser, properties):
-        """Merge the platform information properties into the configuration."""
-        system, _, release, version, machine, processor = platform.uname()
-        system, release, version = platform.system_alias(system, release,
-                                                         version)
-        self.properties['machine'] = machine
-        self.properties['processor'] = processor
-        self.properties['os'] = system
-        self.properties['family'] = os.name
-        self.properties['version'] = release
-
-        mapping = {'machine': ('machine', 'name'),
-                   'processor': ('machine', 'processor'),
-                   'os': ('os', 'name'),
-                   'family': ('os', 'family'),
-                   'version': ('os', 'version')}
-        for key, (section, option) in mapping.items():
-            if parser.has_section(section):
-                value = parser.get(section, option)
-                if value is not None:
-                    self.properties[key] = value
-
-        if properties:
-            for key, value in properties.items():
-                if key in mapping:
-                    self.properties[key] = value
-
-    def _merge_packages(self, parser, properties):
-        """Merge package information into the configuration."""
-        for section in parser.sections():
-            if section in ('os', 'machine', 'maintainer'):
-                continue
-            package = {}
-            for option in parser.options(section):
-                package[option] = parser.get(section, option)
-            self.packages[section] = package
-
-        if properties:
-            for key, value in properties.items():
-                if '.' in key:
-                    package, propname = key.split('.', 1)
-                    if package not in self.packages:
-                        self.packages[package] = {}
-                    self.packages[package][propname] = value
-
-    def __contains__(self, key):
-        """Return whether the configuration contains a value for the specified
-        key.
-        
-        :param key: name of the configuration option using dotted notation
-                    (for example, "python.path")
-        """
-        if '.' in key:
-            package, propname = key.split('.', 1)
-            return propname in self.packages.get(package, {})
-        return key in self.properties
-
-    def __getitem__(self, key):
-        """Return the value for the specified configuration key.
-        
-        :param key: name of the configuration option using dotted notation
-                    (for example, "python.path")
-        """
-        if '.' in key:
-            package, propname = key.split('.', 1)
-            return self.packages.get(package, {}).get(propname)
-        return self.properties.get(key)
-
-    def __str__(self):
-        return str({'properties': self.properties, 'packages': self.packages})
-
-    def get_dirpath(self, key):
-        """Return the value of the specified configuration key, but verify that
-        the value refers to the path of an existing directory.
-        
-        If the value does not exist, or is not a directory path, return `None`.
-
-        :param key: name of the configuration option using dotted notation
-                    (for example, "ant.home")
-        """
-        dirpath = self[key]
-        if dirpath:
-            if os.path.isdir(dirpath):
-                return dirpath
-            log.warning('Invalid %s: %s is not a directory', key, dirpath)
-        return None
-
-    def get_filepath(self, key):
-        """Return the value of the specified configuration key, but verify that
-        the value refers to the path of an existing file.
-        
-        If the value does not exist, or is not a file path, return `None`.
-
-        :param key: name of the configuration option using dotted notation
-                    (for example, "python.path")
-        """
-        filepath = self[key]
-        if filepath:
-            if os.path.isfile(filepath):
-                return filepath
-            log.warning('Invalid %s: %s is not a file', key, filepath)
-        return None
-
-    _VAR_RE = re.compile(r'\$\{(?P<ref>\w[\w.]*?\w)(?:\:(?P<def>.+))?\}')
-
-    def interpolate(self, text, **vars):
-        """Interpolate configuration properties into a string.
-        
-        Properties can be referenced in the text using the notation
-        ``${property.name}``. A default value can be provided by appending it to
-        the property name separated by a colon, for example
-        ``${property.name:defaultvalue}``. This value will be used when there's
-        no such property in the configuration. Otherwise, if no default is
-        provided, the reference is not replaced at all.
-
-        :param text: the string containing variable references
-        :param vars: extra variables to use for the interpolation
-        """
-        def _replace(m):
-            refname = m.group('ref')
-            if refname in self:
-                return self[refname]
-            elif refname in vars:
-                return vars[refname]
-            elif m.group('def'):
-                return m.group('def')
-            else:
-                return m.group(0)
-        return self._VAR_RE.sub(_replace, text)
deleted file mode 100644
--- a/trac-0.11/bitten/build/ctools.py
+++ /dev/null
@@ -1,365 +0,0 @@
-# -*- 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.
-
-"""Recipe commands for build tasks commonly used for C/C++ projects."""
-
-import logging
-import re
-import os
-import posixpath
-import shlex
-
-from bitten.build import CommandLine, FileSet
-from bitten.util import xmlio
-
-log = logging.getLogger('bitten.build.ctools')
-
-__docformat__ = 'restructuredtext en'
-
-def configure(ctxt, file_='configure', enable=None, disable=None, with=None,
-              without=None, cflags=None, cxxflags=None):
-    """Run a ``configure`` script.
-    
-    :param ctxt: the build context
-    :type ctxt: `Context`
-    :param file\_: name of the configure script
-    :param enable: names of the features to enable, seperated by spaces
-    :param disable: names of the features to disable, separated by spaces
-    :param with: names of external packages to include
-    :param without: names of external packages to exclude
-    :param cflags: ``CFLAGS`` to pass to the configure script
-    :param cxxflags: ``CXXFLAGS`` to pass to the configure script
-    """
-    args = []
-    if enable:
-        args += ['--enable-%s' % feature for feature in enable.split()]
-    if disable:
-        args += ['--disable-%s' % feature for feature in disable.split()]
-    if with:
-        for pkg in with.split():
-            pkg_path = pkg + '.path'
-            if pkg_path in ctxt.config:
-                args.append('--with-%s=%s' % (pkg, ctxt.config[pkg_path]))
-            else:
-                args.append('--with-%s' % pkg)
-    if without:
-        args += ['--without-%s' % pkg for pkg in without.split()]
-    if cflags:
-        args.append('CFLAGS=%s' % cflags)
-    if cxxflags:
-        args.append('CXXFLAGS=%s' % cxxflags)
-
-    from bitten.build import shtools
-    returncode = shtools.execute(ctxt, file_=file_, args=args)
-    if returncode != 0:
-        ctxt.error('configure failed (%s)' % returncode)
-
-def autoreconf(ctxt, file_='configure', force=None, install=None, symlink=None,
-              warnings=None, prepend_include=None, include =None):
-    """Run the autotoll ``autoreconf``.
-    
-    :param ctxt: the build context
-    :type ctxt: `Context`
-    :param force: consider all files obsolete
-    :param install: copy missing auxiliary files
-    :param symlink: install symbolic links instead of copies
-    :param warnings: report the warnings falling in CATEGORY
-    :prepend_include: prepend directories to search path
-    :include: append directories to search path
-
-    """
-    args = []
-    if install:
-        args.append('--install')
-        if symlink:
-            args.append('--symlink')
-    if force:
-        args.append('--force')
-    if warnings:
-        args.append('--warnings=%s' % warnings)
-        
-    if include:
-        args += ['--include=%s' % inc for inc in include.split()]
-    if prepend_include:
-        args += ['--prepend-include=%s' % pinc for pinc in prepend_include.split()]
-            
-    from bitten.build import shtools
-    returncode = shtools.execute(ctxt, 'autoreconf', args=args)
-    if returncode != 0:
-        ctxt.error('autoreconf failed (%s)' % returncode)
-
-def make(ctxt, target=None, file_=None, keep_going=False, directory=None, jobs=None, args=None):
-    """Execute a Makefile target.
-    
-    :param ctxt: the build context
-    :type ctxt: `Context`
-    :param file\_: name of the Makefile
-    :param keep_going: whether make should keep going when errors are
-                       encountered
-    :param directory: directory in which to build; defaults to project source directory
-    :param jobs: number of concurrent jobs to run
-    :param args: command-line arguments to pass to the script
-    """
-    executable = ctxt.config.get_filepath('make.path') or 'make'
-
-    if directory is None:
-        directory = ctxt.basedir
-
-    margs = ['--directory', directory]
-
-    if file_:
-        margs += ['--file', ctxt.resolve(file_)]
-    if keep_going:
-        margs.append('--keep-going')
-    if target:
-        margs.append(target)
-    if jobs:
-        margs += ['--jobs', jobs]
-
-    if args:
-        if isinstance(args, basestring):
-            margs += shlex.split(args)
-
-    from bitten.build import shtools
-    returncode = shtools.execute(ctxt, executable=executable, args=margs)
-    if returncode != 0:
-        ctxt.error('make failed (%s)' % returncode)
-
-def cppunit(ctxt, file_=None, srcdir=None):
-    """Collect CppUnit XML data.
-    
-    :param ctxt: the build context
-    :type ctxt: `Context`
-    :param file\_: path of the file containing the CppUnit results; may contain
-                  globbing wildcards to match multiple files
-    :param srcdir: name of the directory containing the source files, used to
-                   link the test results to the corresponding files
-    """
-    assert file_, 'Missing required attribute "file"'
-
-    try:
-        fileobj = file(ctxt.resolve(file_), 'r')
-        try:
-            total, failed = 0, 0
-            results = xmlio.Fragment()
-            for group in xmlio.parse(fileobj):
-                if group.name not in ('FailedTests', 'SuccessfulTests'):
-                    continue
-                for child in group.children():
-                    test = xmlio.Element('test')
-                    name = child.children('Name').next().gettext()
-                    if '::' in name:
-                        parts = name.split('::')
-                        test.attr['fixture'] = '::'.join(parts[:-1])
-                        name = parts[-1]
-                    test.attr['name'] = name
-
-                    for location in child.children('Location'):
-                        for file_elem in location.children('File'):
-                            filepath = file_elem.gettext()
-                            if srcdir is not None:
-                                filepath = posixpath.join(srcdir, filepath)
-                            test.attr['file'] = filepath
-                            break
-                        for line_elem in location.children('Line'):
-                            test.attr['line'] = line_elem.gettext()
-                            break
-                        break
-
-                    if child.name == 'FailedTest':
-                        for message in child.children('Message'):
-                            test.append(xmlio.Element('traceback')[
-                                message.gettext()
-                            ])
-                        test.attr['status'] = 'failure'
-                        failed += 1
-                    else:
-                        test.attr['status'] = 'success'
-
-                    results.append(test)
-                    total += 1
-
-            if failed:
-                ctxt.error('%d of %d test%s failed' % (failed, total,
-                           total != 1 and 's' or ''))
-
-            ctxt.report('test', results)
-
-        finally:
-            fileobj.close()
-
-    except IOError, e:
-        log.warning('Error opening CppUnit results file (%s)', e)
-    except xmlio.ParseError, e:
-        print e
-        log.warning('Error parsing CppUnit results file (%s)', e)
-
-def cunit (ctxt, file_=None, srcdir=None):
-    """Collect CUnit XML data.
-    
-    :param ctxt: the build context
-    :type ctxt: `Context`
-    :param file\_: path of the file containing the CUnit results; may contain
-                  globbing wildcards to match multiple files
-    :param srcdir: name of the directory containing the source files, used to
-                   link the test results to the corresponding files
-    """
-    assert file_, 'Missing required attribute "file"'
-
-    try:
-        fileobj = file(ctxt.resolve(file_), 'r')
-        try:
-            total, failed = 0, 0
-            results = xmlio.Fragment()
-            log_elem = xmlio.Fragment()
-            def info (msg):
-                log.info (msg)
-                log_elem.append (xmlio.Element ('message', level='info')[msg])
-            def warning (msg):
-                log.warning (msg)
-                log_elem.append (xmlio.Element ('message', level='warning')[msg])
-            def error (msg):
-                log.error (msg)
-                log_elem.append (xmlio.Element ('message', level='error')[msg])
-            for node in xmlio.parse(fileobj):
-                if node.name != 'CUNIT_RESULT_LISTING':
-                    continue
-                for suiteRun in node.children ('CUNIT_RUN_SUITE'):
-                    for suite in suiteRun.children():
-                        if suite.name not in ('CUNIT_RUN_SUITE_SUCCESS', 'CUNIT_RUN_SUITE_FAILURE'):
-                            warning ("Unknown node: %s" % suite.name)
-                            continue
-                        suiteName = suite.children ('SUITE_NAME').next().gettext()
-                        info ("%s [%s]" % ("*" * (57 - len (suiteName)), suiteName))
-                        for record in suite.children ('CUNIT_RUN_TEST_RECORD'):
-                            for result in record.children():
-                                if result.name not in ('CUNIT_RUN_TEST_SUCCESS', 'CUNIT_RUN_TEST_FAILURE'):
-                                    continue
-                                testName = result.children ('TEST_NAME').next().gettext()
-                                info ("Running %s..." % testName);
-                                test = xmlio.Element('test')
-                                test.attr['fixture'] = suiteName
-                                test.attr['name'] = testName
-                                if result.name == 'CUNIT_RUN_TEST_FAILURE':
-                                    error ("%s(%d): %s"
-                                               % (result.children ('FILE_NAME').next().gettext(),
-                                                  int (result.children ('LINE_NUMBER').next().gettext()),
-                                                  result.children ('CONDITION').next().gettext()))
-                                    test.attr['status'] = 'failure'
-                                    failed += 1
-                                else:
-                                    test.attr['status'] = 'success'
-
-                                results.append(test)
-                                total += 1
-
-            if failed:
-                ctxt.error('%d of %d test%s failed' % (failed, total,
-                           total != 1 and 's' or ''))
-
-            ctxt.report('test', results)
-            ctxt.log (log_elem)
-
-        finally:
-            fileobj.close()
-
-    except IOError, e:
-        log.warning('Error opening CUnit results file (%s)', e)
-    except xmlio.ParseError, e:
-        print e
-        log.warning('Error parsing CUnit results file (%s)', e)
-
-def gcov(ctxt, include=None, exclude=None, prefix=None, root=""):
-    """Run ``gcov`` to extract coverage data where available.
-    
-    :param ctxt: the build context
-    :type ctxt: `Context`
-    :param include: patterns of files and directories to include
-    :param exclude: patterns of files and directories that should be excluded
-    :param prefix: optional prefix name that is added to object files by the
-                   build system
-    :param root: optional root path in which the build system puts the object
-                 files
-    """
-    file_re = re.compile(r'^File (?:\'|\`)(?P<file>[^\']+)\'\s*$')
-    lines_re = re.compile(r'^Lines executed:(?P<cov>\d+\.\d+)\% of (?P<num>\d+)\s*$')
-
-    files = []
-    for filename in FileSet(ctxt.basedir, include, exclude):
-        if os.path.splitext(filename)[1] in ('.c', '.cpp', '.cc', '.cxx'):
-            files.append(filename)
-
-    coverage = xmlio.Fragment()
-    log_elem = xmlio.Fragment()
-    def info (msg):
-        log.info (msg)
-        log_elem.append (xmlio.Element ('message', level='info')[msg])
-    def warning (msg):
-        log.warning (msg)
-        log_elem.append (xmlio.Element ('message', level='warning')[msg])
-    def error (msg):
-        log.error (msg)
-        log_elem.append (xmlio.Element ('message', level='error')[msg])
-
-    for srcfile in files:
-        # Determine the coverage for each source file by looking for a .gcno
-        # and .gcda pair
-        info ("Getting coverage info for %s" % srcfile)
-        filepath, filename = os.path.split(srcfile)
-        stem = os.path.splitext(filename)[0]
-        if prefix is not None:
-            stem = prefix + '-' + stem
-
-        objfile = os.path.join (root, filepath, stem + '.o')
-        if not os.path.isfile(ctxt.resolve(objfile)):
-            warning ('No object file found for %s at %s' % (srcfile, objfile))
-            continue
-        if not os.path.isfile (ctxt.resolve (os.path.join (root, filepath, stem + '.gcno'))):
-            warning ('No .gcno file found for %s at %s' % (srcfile, os.path.join (root, filepath, stem + '.gcno')))
-            continue
-        if not os.path.isfile (ctxt.resolve (os.path.join (root, filepath, stem + '.gcda'))):
-            warning ('No .gcda file found for %s at %s' % (srcfile, os.path.join (root, filepath, stem + '.gcda')))
-            continue
-
-        num_lines, num_covered = 0, 0
-        skip_block = False
-        cmd = CommandLine('gcov', ['-b', '-n', '-o', objfile, srcfile],
-                          cwd=ctxt.basedir)
-        for out, err in cmd.execute():
-            if out == '': # catch blank lines, reset the block state...
-                skip_block = False
-            elif out and not skip_block:
-                # Check for a file name
-                match = file_re.match(out)
-                if match:
-                    if os.path.isabs(match.group('file')):
-                        skip_block = True
-                        continue
-                else:
-                    # check for a "Lines executed" message
-                    match = lines_re.match(out)
-                    if match:
-                        lines = float(match.group('num'))
-                        cov = float(match.group('cov'))
-                        num_covered += int(lines * cov / 100)
-                        num_lines += int(lines)
-        if cmd.returncode != 0:
-            continue
-
-        module = xmlio.Element('coverage', name=os.path.basename(srcfile),
-                                file=srcfile.replace(os.sep, '/'),
-                                lines=num_lines, percentage=0)
-        if num_lines:
-            percent = int(round(num_covered * 100 / num_lines))
-            module.attr['percentage'] = percent
-        coverage.append(module)
-
-    ctxt.report('coverage', coverage)
-    ctxt.log (log_elem)
deleted file mode 100644
--- a/trac-0.11/bitten/build/javatools.py
+++ /dev/null
@@ -1,225 +0,0 @@
-# -*- coding: utf-8 -*-
-#
-# Copyright (C) 2005-2007 Christopher Lenz <cmlenz@gmx.de>
-# Copyright (C) 2006 Matthew Good <matt@matt-good.net>
-# 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.
-
-"""Recipe commands for tools commonly used in Java projects."""
-
-from glob import glob
-import logging
-import os
-import posixpath
-import shlex
-import tempfile
-
-from bitten.build import CommandLine
-from bitten.util import xmlio
-
-log = logging.getLogger('bitten.build.javatools')
-
-__docformat__ = 'restructuredtext en'
-
-def ant(ctxt, file_=None, target=None, keep_going=False, args=None):
-    """Run an Ant build.
-    
-    :param ctxt: the build context
-    :type ctxt: `Context`
-    :param file\_: name of the Ant build file
-    :param target: name of the target that should be executed (optional)
-    :param keep_going: whether Ant should keep going when errors are encountered
-    :param args: additional arguments to pass to Ant
-    """
-    executable = 'ant'
-    ant_home = ctxt.config.get_dirpath('ant.home')
-    if ant_home:
-        executable = os.path.join(ant_home, 'bin', 'ant')
-
-    java_home = ctxt.config.get_dirpath('java.home')
-    if java_home:
-        os.environ['JAVA_HOME'] = java_home
-
-    logfile = tempfile.NamedTemporaryFile(prefix='ant_log', suffix='.xml')
-    logfile.close()
-    if args:
-        args = shlex.split(args)
-    else:
-        args = []
-    args += ['-noinput', '-listener', 'org.apache.tools.ant.XmlLogger',
-             '-Dant.XmlLogger.stylesheet.uri', '""',
-             '-DXmlLogger.file', logfile.name]
-    if file_:
-        args += ['-buildfile', ctxt.resolve(file_)]
-    if keep_going:
-        args.append('-keep-going')
-    if target:
-        args.append(target)
-
-    cmdline = CommandLine(executable, args, cwd=ctxt.basedir)
-    for out, err in cmdline.execute():
-        if out is not None:
-            log.info(out)
-        if err is not None:
-            log.error(err)
-
-    error_logged = False
-    log_elem = xmlio.Fragment()
-    try:
-        xml_log = xmlio.parse(file(logfile.name, 'r'))
-        def collect_log_messages(node):
-            for child in node.children():
-                if child.name == 'message':
-                    if child.attr['priority'] == 'debug':
-                        continue
-                    log_elem.append(xmlio.Element('message',
-                                                  level=child.attr['priority'])[
-                        child.gettext().replace(ctxt.basedir + os.sep, '')
-                                       .replace(ctxt.basedir, '')
-                    ])
-                else:
-                    collect_log_messages(child)
-        collect_log_messages(xml_log)
-
-        if 'error' in xml_log.attr:
-            ctxt.error(xml_log.attr['error'])
-            error_logged = True
-
-    except xmlio.ParseError, e:
-        log.warning('Error parsing Ant XML log file (%s)', e)
-    ctxt.log(log_elem)
-
-    if not error_logged and cmdline.returncode != 0:
-        ctxt.error('Ant failed (%s)' % cmdline.returncode)
-
-def junit(ctxt, file_=None, srcdir=None):
-    """Extract test results from a JUnit XML report.
-    
-    :param ctxt: the build context
-    :type ctxt: `Context`
-    :param file\_: path to the JUnit XML test results; may contain globbing
-                  wildcards for matching multiple results files
-    :param srcdir: name of the directory containing the test sources, used to
-                   link test results to the corresponding source files
-    """
-    assert file_, 'Missing required attribute "file"'
-    try:
-        total, failed = 0, 0
-        results = xmlio.Fragment()
-        for path in glob(ctxt.resolve(file_)):
-            fileobj = file(path, 'r')
-            try:
-                for testcase in xmlio.parse(fileobj).children('testcase'):
-                    test = xmlio.Element('test')
-                    test.attr['fixture'] = testcase.attr['classname']
-                    if 'time' in testcase.attr:
-                        test.attr['duration'] = testcase.attr['time']
-                    if srcdir is not None:
-                        cls = testcase.attr['classname'].split('.')
-                        test.attr['file'] = posixpath.join(srcdir, *cls) + \
-                                            '.java'
-
-                    result = list(testcase.children())
-                    if result:
-                        test.attr['status'] = result[0].name
-                        test.append(xmlio.Element('traceback')[
-                            result[0].gettext()
-                        ])
-                        failed += 1
-                    else:
-                        test.attr['status'] = 'success'
-
-                    results.append(test)
-                    total += 1
-            finally:
-                fileobj.close()
-        if failed:
-            ctxt.error('%d of %d test%s failed' % (failed, total,
-                       total != 1 and 's' or ''))
-        ctxt.report('test', results)
-    except IOError, e:
-        log.warning('Error opening JUnit results file (%s)', e)
-    except xmlio.ParseError, e:
-        log.warning('Error parsing JUnit results file (%s)', e)
-
-
-class _LineCounter(object):
-    def __init__(self):
-        self.lines = []
-        self.covered = 0
-        self.num_lines = 0
-
-    def __getitem__(self, idx):
-        if idx >= len(self.lines):
-            return 0
-        return self.lines[idx]
-
-    def __setitem__(self, idx, val):
-        idx = int(idx) - 1 # 1-indexed to 0-indexed
-        from itertools import repeat
-        if idx >= len(self.lines):
-            self.lines.extend(repeat('-', idx - len(self.lines) + 1))
-        self.lines[idx] = val
-        self.num_lines += 1
-        if val != '0':
-            self.covered += 1
-
-    def line_hits(self):
-        return ' '.join(self.lines)
-    line_hits = property(line_hits)
-
-    def percentage(self):
-        if self.num_lines == 0:
-            return 0
-        return int(round(self.covered * 100. / self.num_lines))
-    percentage = property(percentage)
-
-
-def cobertura(ctxt, file_=None):
-    """Extract test coverage information from a Cobertura XML report.
-    
-    :param ctxt: the build context
-    :type ctxt: `Context`
-    :param file\_: path to the Cobertura XML output
-    """
-    assert file_, 'Missing required attribute "file"'
-
-    coverage = xmlio.Fragment()
-    doc = xmlio.parse(open(ctxt.resolve(file_)))
-    srcdir = [s.gettext().strip() for ss in doc.children('sources')
-                                  for s in ss.children('source')][0]
-
-    classes = [cls for pkgs in doc.children('packages')
-                   for pkg in pkgs.children('package')
-                   for clss in pkg.children('classes')
-                   for cls in clss.children('class')]
-
-    counters = {}
-    class_names = {}
-
-    for cls in classes:
-        filename = cls.attr['filename'].replace(os.sep, '/')
-        name = cls.attr['name']
-        if not '$' in name: # ignore internal classes
-            class_names[filename] = name
-        counter = counters.get(filename)
-        if counter is None:
-            counter = counters[filename] = _LineCounter()
-        lines = [l for ls in cls.children('lines')
-                   for l in ls.children('line')]
-        for line in lines:
-            counter[line.attr['number']] = line.attr['hits']
-
-    for filename, name in class_names.iteritems():
-        counter = counters[filename]
-        module = xmlio.Element('coverage', name=name,
-                               file=posixpath.join(srcdir, filename),
-                               lines=counter.num_lines,
-                               percentage=counter.percentage)
-        module.append(xmlio.Element('line_hits')[counter.line_hits])
-        coverage.append(module)
-    ctxt.report('coverage', coverage)
deleted file mode 100644
--- a/trac-0.11/bitten/build/phptools.py
+++ /dev/null
@@ -1,113 +0,0 @@
-# -*- coding: UTF-8 -*-
-#
-# Copyright (C) 2007 Edgewall Software
-# Copyright (C) 2007 Wei Zhuo <weizhuo@gmail.com>
-# 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.cmlenz.net/wiki/License.
-
-import logging
-import os
-import shlex
-
-from bitten.util import xmlio
-from bitten.build import shtools
-
-log = logging.getLogger('bitten.build.phptools')
-
-def phing(ctxt, file_=None, target=None, executable=None, args=None):
-    """Run a phing build"""
-    if args:
-        args = shlex.split(args)
-    else:
-        args = []
-    args += ['-logger', 'phing.listener.DefaultLogger',
-             '-buildfile', ctxt.resolve(file_ or 'build.xml')]
-    if target:
-        args.append(target)
-
-    returncode = shtools.execute(ctxt, file_=executable or 'phing', args=args)
-    if returncode != 0:
-        ctxt.error('Phing failed (%s)' % returncode)
-
-def phpunit(ctxt, file_=None):
-    """Extract test results from a PHPUnit XML report."""
-    assert file_, 'Missing required attribute "file"'
-    try:
-        total, failed = 0, 0
-        results = xmlio.Fragment()
-        fileobj = file(ctxt.resolve(file_), 'r')
-        try:
-            for testsuit in xmlio.parse(fileobj).children('testsuite'):
-                total += int(testsuit.attr['tests'])
-                failed += int(testsuit.attr['failures']) + \
-                            int(testsuit.attr['errors'])
-
-                for testcase in testsuit.children():
-                    test = xmlio.Element('test')
-                    test.attr['fixture'] = testcase.attr['class']
-                    test.attr['name'] = testcase.attr['name']
-                    test.attr['duration'] = testcase.attr['time']
-                    result = list(testcase.children())
-                    if result:
-                        test.append(xmlio.Element('traceback')[
-                            result[0].gettext()
-                        ])
-                        test.attr['status'] = result[0].name
-                    else:
-                        test.attr['status'] = 'success'
-                    if 'file' in testsuit.attr:
-                        testfile = os.path.realpath(testsuit.attr['file'])
-                        if testfile.startswith(ctxt.basedir):
-                            testfile = testfile[len(ctxt.basedir) + 1:]
-                        testfile = testfile.replace(os.sep, '/')
-                        test.attr['file'] = testfile
-                    results.append(test)
-        finally:
-            fileobj.close()
-        if failed:
-            ctxt.error('%d of %d test%s failed' % (failed, total,
-                        total != 1 and 's' or ''))
-        ctxt.report('test', results)
-    except IOError, e:
-        ctxt.log('Error opening PHPUnit results file (%s)' % e)
-    except xmlio.ParseError, e:
-        ctxt.log('Error parsing PHPUnit results file (%s)' % e)
-
-def coverage(ctxt, file_=None):
-    """Extract data from a Phing code coverage report."""
-    assert file_, 'Missing required attribute "file"'
-    try:
-        summary_file = file(ctxt.resolve(file_), 'r')
-        try:
-            coverage = xmlio.Fragment()
-            for package in xmlio.parse(summary_file).children('package'):
-                for cls in package.children('class'):
-                    statements = float(cls.attr['statementcount'])
-                    covered = float(cls.attr['statementscovered'])
-                    if statements:
-                        percentage = covered / statements * 100
-                    else:
-                        percentage = 100
-                    class_coverage = xmlio.Element('coverage',
-                        name=cls.attr['name'],
-                        lines=int(statements),
-                        percentage=percentage
-                    )
-                    source = list(cls.children())[0]
-                    if 'sourcefile' in source.attr:
-                        sourcefile = os.path.realpath(source.attr['sourcefile'])
-                        if sourcefile.startswith(ctxt.basedir):
-                            sourcefile = sourcefile[len(ctxt.basedir) + 1:]
-                        sourcefile = sourcefile.replace(os.sep, '/')
-                        class_coverage.attr['file'] = sourcefile
-                    coverage.append(class_coverage)
-        finally:
-            summary_file.close()
-        ctxt.report('coverage', coverage)
-    except IOError, e:
-        ctxt.log('Error opening coverage summary file (%s)' % e)
-    except xmlio.ParseError, e:
-        ctxt.log('Error parsing coverage summary file (%s)' % e)
deleted file mode 100644
--- a/trac-0.11/bitten/build/pythontools.py
+++ /dev/null
@@ -1,466 +0,0 @@
-# -*- coding: utf-8 -*-
-#
-# Copyright (C) 2005-2007 Christopher Lenz <cmlenz@gmx.de>
-# Copyright (C) 2008 Matt Good <matt@matt-good.net>
-# Copyright (C) 2008 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.
-
-"""Recipe commands for tools commonly used by Python projects."""
-
-from __future__ import division
-
-import logging
-import os
-import cPickle as pickle
-import re
-try:
-    set
-except NameError:
-    from sets import Set as set
-import shlex
-import sys
-
-from bitten.build import CommandLine, FileSet
-from bitten.util import loc, xmlio
-
-log = logging.getLogger('bitten.build.pythontools')
-
-__docformat__ = 'restructuredtext en'
-
-def _python_path(ctxt):
-    """Return the path to the Python interpreter.
-    
-    If the configuration has a ``python.path`` property, the value of that
-    option is returned; otherwise the path to the current Python interpreter is
-    returned.
-    """
-    python_path = ctxt.config.get_filepath('python.path')
-    if python_path:
-        return python_path
-    return sys.executable
-
-def distutils(ctxt, file_='setup.py', command='build', options=None):
-    """Execute a ``distutils`` command.
-    
-    :param ctxt: the build context
-    :type ctxt: `Context`
-    :param file\_: name of the file defining the distutils setup
-    :param command: the setup command to execute
-    :param options: additional options to pass to the command
-    """
-    if options:
-        if isinstance(options, basestring):
-            options = shlex.split(options)
-    else:
-        options = []
-
-    cmdline = CommandLine(_python_path(ctxt),
-                          [ctxt.resolve(file_), command] + options,
-                          cwd=ctxt.basedir)
-    log_elem = xmlio.Fragment()
-    error_logged = False
-    for out, err in cmdline.execute():
-        if out is not None:
-            log.info(out)
-            log_elem.append(xmlio.Element('message', level='info')[out])
-        if err is not None:
-            level = 'error'
-            if err.startswith('warning: '):
-                err = err[9:]
-                level = 'warning'
-                log.warning(err)
-            elif err.startswith('error: '):
-                ctxt.error(err[7:])
-                error_logged = True
-            else:
-                log.error(err)
-            log_elem.append(xmlio.Element('message', level=level)[err])
-    ctxt.log(log_elem)
-
-    if not error_logged and cmdline.returncode != 0:
-        ctxt.error('distutils failed (%s)' % cmdline.returncode)
-
-def exec_(ctxt, file_=None, module=None, function=None, output=None, args=None):
-    """Execute a Python script.
-    
-    Either the `file_` or the `module` parameter must be provided. If
-    specified using the `file_` parameter, the file must be inside the project
-    directory. If specified as a module, the module must either be resolvable
-    to a file, or the `function` parameter must be provided
-    
-    :param ctxt: the build context
-    :type ctxt: `Context`
-    :param file\_: name of the script file to execute
-    :param module: name of the Python module to execute
-    :param function: name of the Python function to run
-    :param output: name of the file to which output should be written
-    :param args: extra arguments to pass to the script
-    """
-    assert file_ or module, 'Either "file" or "module" attribute required'
-    if function:
-        assert module and not file_, '"module" attribute required for use of ' \
-                                     '"function" attribute'
-
-    if module:
-        # Script specified as module name, need to resolve that to a file,
-        # or use the function name if provided
-        if function:
-            args = '-c "import sys; from %s import %s; %s(sys.argv)" %s' % (
-                   module, function, function, args)
-        else:
-            try:
-                mod = __import__(module, globals(), locals(), [])
-                components = module.split('.')
-                for comp in components[1:]:
-                    mod = getattr(mod, comp)
-                file_ = mod.__file__.replace('\\', '/')
-            except ImportError, e:
-                ctxt.error('Cannot execute Python module %s: %s' % (module, e))
-                return
-
-    from bitten.build import shtools
-    returncode = shtools.execute(ctxt, executable=_python_path(ctxt),
-                                 file_=file_, output=output, args=args)
-    if returncode != 0:
-        ctxt.error('Executing %s failed (error code %s)' % (file_, returncode))
-
-def pylint(ctxt, file_=None):
-    """Extract data from a ``pylint`` run written to a file.
-    
-    :param ctxt: the build context
-    :type ctxt: `Context`
-    :param file\_: name of the file containing the Pylint output
-    """
-    assert file_, 'Missing required attribute "file"'
-    msg_re = re.compile(r'^(?P<file>.+):(?P<line>\d+): '
-                        r'\[(?P<type>[A-Z]\d*)(?:, (?P<tag>[\w\.]+))?\] '
-                        r'(?P<msg>.*)$')
-    msg_categories = dict(W='warning', E='error', C='convention', R='refactor')
-
-    problems = xmlio.Fragment()
-    try:
-        fd = open(ctxt.resolve(file_), 'r')
-        try:
-            for line in fd:
-                match = msg_re.search(line)
-                if match:
-                    msg_type = match.group('type')
-                    category = msg_categories.get(msg_type[0])
-                    if len(msg_type) == 1:
-                        msg_type = None
-                    filename = os.path.realpath(match.group('file'))
-                    if filename.startswith(ctxt.basedir):
-                        filename = filename[len(ctxt.basedir) + 1:]
-                    filename = filename.replace(os.sep, '/')
-                    lineno = int(match.group('line'))
-                    tag = match.group('tag')
-                    problems.append(xmlio.Element('problem', category=category,
-                                                  type=msg_type, tag=tag,
-                                                  line=lineno, file=filename)[
-                        match.group('msg') or ''
-                    ])
-            ctxt.report('lint', problems)
-        finally:
-            fd.close()
-    except IOError, e:
-        log.warning('Error opening pylint results file (%s)', e)
-
-def coverage(ctxt, summary=None, coverdir=None, include=None, exclude=None):
-    """Extract data from a ``coverage.py`` run.
-    
-    :param ctxt: the build context
-    :type ctxt: `Context`
-    :param summary: path to the file containing the coverage summary
-    :param coverdir: name of the directory containing the per-module coverage
-                     details
-    :param include: patterns of files or directories to include in the report
-    :param exclude: patterns of files or directories to exclude from the report
-    """
-    assert summary, 'Missing required attribute "summary"'
-
-    summary_line_re = re.compile(r'^(?P<module>.*?)\s+(?P<stmts>\d+)\s+'
-                                 r'(?P<exec>\d+)\s+(?P<cov>\d+)%\s+'
-                                 r'(?:(?P<missing>(?:\d+(?:-\d+)?(?:, )?)*)\s+)?'
-                                 r'(?P<file>.+)$')
-
-    fileset = FileSet(ctxt.basedir, include, exclude)
-    missing_files = []
-    for filename in fileset:
-        if os.path.splitext(filename)[1] != '.py':
-            continue
-        missing_files.append(filename)
-    covered_modules = set()
-
-    try:
-        summary_file = open(ctxt.resolve(summary), 'r')
-        try:
-            coverage = xmlio.Fragment()
-            for summary_line in summary_file:
-                match = summary_line_re.search(summary_line)
-                if match:
-                    modname = match.group(1)
-                    filename = match.group(6)
-                    if not os.path.isabs(filename):
-                        filename = os.path.normpath(os.path.join(ctxt.basedir,
-                                                                 filename))
-                    else:
-                        filename = os.path.realpath(filename)
-                    if not filename.startswith(ctxt.basedir):
-                        continue
-                    filename = filename[len(ctxt.basedir) + 1:]
-                    if not filename in fileset:
-                        continue
-
-                    percentage = int(match.group(4).rstrip('%'))
-                    num_lines = int(match.group(2))
-
-                    missing_files.remove(filename)
-                    covered_modules.add(modname)
-                    module = xmlio.Element('coverage', name=modname,
-                                           file=filename.replace(os.sep, '/'),
-                                           percentage=percentage,
-                                           lines=num_lines)
-                    coverage.append(module)
-
-            for filename in missing_files:
-                modname = os.path.splitext(filename.replace(os.sep, '.'))[0]
-                if modname in covered_modules:
-                    continue
-                covered_modules.add(modname)
-                module = xmlio.Element('coverage', name=modname,
-                                       file=filename.replace(os.sep, '/'),
-                                       percentage=0)
-                coverage.append(module)
-
-            ctxt.report('coverage', coverage)
-        finally:
-            summary_file.close()
-    except IOError, e:
-        log.warning('Error opening coverage summary file (%s)', e)
-
-def trace(ctxt, summary=None, coverdir=None, include=None, exclude=None):
-    """Extract data from a ``trace.py`` run.
-    
-    :param ctxt: the build context
-    :type ctxt: `Context`
-    :param summary: path to the file containing the coverage summary
-    :param coverdir: name of the directory containing the per-module coverage
-                     details
-    :param include: patterns of files or directories to include in the report
-    :param exclude: patterns of files or directories to exclude from the report
-    """
-    assert summary, 'Missing required attribute "summary"'
-    assert coverdir, 'Missing required attribute "coverdir"'
-
-    summary_line_re = re.compile(r'^\s*(?P<lines>\d+)\s+(?P<cov>\d+)%\s+'
-                                 r'(?P<module>.*?)\s+\((?P<filename>.*?)\)')
-    coverage_line_re = re.compile(r'\s*(?:(?P<hits>\d+): )?(?P<line>.*)')
-
-    fileset = FileSet(ctxt.basedir, include, exclude)
-    missing_files = []
-    for filename in fileset:
-        if os.path.splitext(filename)[1] != '.py':
-            continue
-        missing_files.append(filename)
-    covered_modules = set()
-
-    def handle_file(elem, sourcefile, coverfile=None):
-        code_lines = set()
-        for lineno, linetype, line in loc.count(sourcefile):
-            if linetype == loc.CODE:
-                code_lines.add(lineno)
-        num_covered = 0
-        lines = []
-
-        if coverfile:
-            prev_hits = '0'
-            for idx, coverline in enumerate(coverfile):
-                match = coverage_line_re.search(coverline)
-                if match:
-                    hits = match.group(1)
-                    if hits: # Line covered
-                        if hits != '0':
-                            num_covered += 1
-                        lines.append(hits)
-                        prev_hits = hits
-                    elif coverline.startswith('>'): # Line not covered
-                        lines.append('0')
-                        prev_hits = '0'
-                    elif idx not in code_lines: # Not a code line
-                        lines.append('-')
-                        prev_hits = '0'
-                    else: # A code line not flagged by trace.py
-                        if prev_hits != '0':
-                            num_covered += 1
-                        lines.append(prev_hits)
-
-            elem.append(xmlio.Element('line_hits')[' '.join(lines)])
-
-        num_lines = len(code_lines)
-        if num_lines:
-            percentage = int(round(num_covered * 100 / num_lines))
-        else:
-            percentage = 0
-        elem.attr['percentage'] = percentage
-        elem.attr['lines'] = num_lines
-
-    try:
-        summary_file = open(ctxt.resolve(summary), 'r')
-        try:
-            coverage = xmlio.Fragment()
-            for summary_line in summary_file:
-                match = summary_line_re.search(summary_line)
-                if match:
-                    modname = match.group(3)
-                    filename = match.group(4)
-                    if not os.path.isabs(filename):
-                        filename = os.path.normpath(os.path.join(ctxt.basedir,
-                                                                 filename))
-                    else:
-                        filename = os.path.realpath(filename)
-                    if not filename.startswith(ctxt.basedir):
-                        continue
-                    filename = filename[len(ctxt.basedir) + 1:]
-                    if not filename in fileset:
-                        continue
-
-                    missing_files.remove(filename)
-                    covered_modules.add(modname)
-                    module = xmlio.Element('coverage', name=modname,
-                                           file=filename.replace(os.sep, '/'))
-                    sourcefile = file(ctxt.resolve(filename))
-                    try:
-                        coverpath = ctxt.resolve(coverdir, modname + '.cover')
-                        if os.path.isfile(coverpath):
-                            coverfile = file(coverpath, 'r')
-                        else:
-                            log.warning('No coverage file for module %s at %s',
-                                        modname, coverpath)
-                            coverfile = None
-                        try:
-                            handle_file(module, sourcefile, coverfile)
-                        finally:
-                            if coverfile:
-                                coverfile.close()
-                    finally:
-                        sourcefile.close()
-                    coverage.append(module)
-
-            for filename in missing_files:
-                modname = os.path.splitext(filename.replace(os.sep, '.'))[0]
-                if modname in covered_modules:
-                    continue
-                covered_modules.add(modname)
-                module = xmlio.Element('coverage', name=modname,
-                                       file=filename.replace(os.sep, '/'),
-                                       percentage=0)
-                filepath = ctxt.resolve(filename)
-                fileobj = file(filepath, 'r')
-                try:
-                    handle_file(module, fileobj)
-                finally:
-                    fileobj.close()
-                coverage.append(module)
-
-            ctxt.report('coverage', coverage)
-        finally:
-            summary_file.close()
-    except IOError, e:
-        log.warning('Error opening coverage summary file (%s)', e)
-
-def figleaf(ctxt, summary=None, include=None, exclude=None):
-    from figleaf import get_lines
-    coverage = xmlio.Fragment()
-    try:
-        fileobj = open(ctxt.resolve(summary))
-    except IOError, e:
-        log.warning('Error opening coverage summary file (%s)', e)
-        return
-    coverage_data = pickle.load(fileobj)
-    fileset = FileSet(ctxt.basedir, include, exclude)
-    for filename in fileset:
-        base, ext = os.path.splitext(filename)
-        if ext != '.py':
-            continue
-        modname = base.replace(os.path.sep, '.')
-        realfilename = ctxt.resolve(filename)
-        interesting_lines = get_lines(open(realfilename))
-        covered_lines = coverage_data.get(realfilename, set())
-        percentage = int(round(len(covered_lines) * 100 / len(interesting_lines)))
-        line_hits = []
-        for lineno in xrange(1, max(interesting_lines)+1):
-            if lineno not in interesting_lines:
-                line_hits.append('-')
-            elif lineno in covered_lines:
-                line_hits.append('1')
-            else:
-                line_hits.append('0')
-        module = xmlio.Element('coverage', name=modname,
-                               file=filename,
-                               percentage=percentage,
-                               lines=len(interesting_lines),
-                               line_hits=' '.join(line_hits))
-        coverage.append(module)
-    ctxt.report('coverage', coverage)
-
-def _normalize_filenames(ctxt, filenames, fileset):
-    for filename in filenames:
-        if not os.path.isabs(filename):
-            filename = os.path.normpath(os.path.join(ctxt.basedir,
-                                                     filename))
-        else:
-            filename = os.path.realpath(filename)
-        if not filename.startswith(ctxt.basedir):
-            continue
-        filename = filename[len(ctxt.basedir) + 1:]
-        if filename not in fileset:
-            continue
-        yield filename.replace(os.sep, '/')
-
-def unittest(ctxt, file_=None):
-    """Extract data from a unittest results file in XML format.
-    
-    :param ctxt: the build context
-    :type ctxt: `Context`
-    :param file\_: name of the file containing the test results
-    """
-    assert file_, 'Missing required attribute "file"'
-
-    try:
-        fileobj = file(ctxt.resolve(file_), 'r')
-        try:
-            total, failed = 0, 0
-            results = xmlio.Fragment()
-            for child in xmlio.parse(fileobj).children():
-                test = xmlio.Element('test')
-                for name, value in child.attr.items():
-                    if name == 'file':
-                        value = os.path.realpath(value)
-                        if value.startswith(ctxt.basedir):
-                            value = value[len(ctxt.basedir) + 1:]
-                            value = value.replace(os.sep, '/')
-                        else:
-                            continue
-                    test.attr[name] = value
-                    if name == 'status' and value in ('error', 'failure'):
-                        failed += 1
-                for grandchild in child.children():
-                    test.append(xmlio.Element(grandchild.name)[
-                        grandchild.gettext()
-                    ])
-                results.append(test)
-                total += 1
-            if failed:
-                ctxt.error('%d of %d test%s failed' % (failed, total,
-                           total != 1 and 's' or ''))
-            ctxt.report('test', results)
-        finally:
-            fileobj.close()
-    except IOError, e:
-        log.warning('Error opening unittest results file (%s)', e)
-    except xmlio.ParseError, e:
-        log.warning('Error parsing unittest results file (%s)', e)
deleted file mode 100644
--- a/trac-0.11/bitten/build/shtools.py
+++ /dev/null
@@ -1,158 +0,0 @@
-# -*- 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.
-
-"""Generic recipe commands for executing external processes."""
-
-import logging
-import os
-import shlex
-
-from bitten.build import CommandLine
-from bitten.util import xmlio
-
-log = logging.getLogger('bitten.build.shtools')
-
-__docformat__ = 'restructuredtext en'
-
-def exec_(ctxt, executable=None, file_=None, output=None, args=None, dir_=None):
-    """Execute a program or shell script.
-    
-    :param ctxt: the build context
-    :type ctxt: `Context`
-    :param executable: name of the executable to run
-    :param file\_: name of the script file, relative to the project directory,
-                  that should be run
-    :param output: name of the file to which the output of the script should be
-                   written
-    :param args: command-line arguments to pass to the script
-    """
-    assert executable or file_, \
-        'Either "executable" or "file" attribute required'
-
-    returncode = execute(ctxt, executable=executable, file_=file_,
-                         output=output, args=args, dir_=dir_)
-    if returncode != 0:
-        ctxt.error('Executing %s failed (error code %s)' % (executable or file_,
-                                                            returncode))
-
-def pipe(ctxt, executable=None, file_=None, input_=None, output=None,
-         args=None, dir_=None):
-    """Pipe the contents of a file through a program or shell script.
-    
-    :param ctxt: the build context
-    :type ctxt: `Context`
-    :param executable: name of the executable to run
-    :param file\_: name of the script file, relative to the project directory,
-                  that should be run
-    :param input\_: name of the file containing the data that should be passed
-                   to the shell script on its standard input stream
-    :param output: name of the file to which the output of the script should be
-                   written
-    :param args: command-line arguments to pass to the script
-    """
-    assert executable or file_, \
-        'Either "executable" or "file" attribute required'
-    assert input_, 'Missing required attribute "input"'
-
-    returncode = execute(ctxt, executable=executable, file_=file_,
-                         input_=input_, output=output, args=args, dir_=dir_)
-    if returncode != 0:
-        ctxt.error('Piping through %s failed (error code %s)'
-                   % (executable or file_, returncode))
-
-def execute(ctxt, executable=None, file_=None, input_=None, output=None,
-            args=None, dir_=None, filter_=None):
-    """Generic external program execution.
-    
-    This function is not itself bound to a recipe command, but rather used from
-    other commands.
-    
-    :param ctxt: the build context
-    :type ctxt: `Context`
-    :param executable: name of the executable to run
-    :param file\_: name of the script file, relative to the project directory,
-                  that should be run
-    :param input\_: name of the file containing the data that should be passed
-                   to the shell script on its standard input stream
-    :param output: name of the file to which the output of the script should be
-                   written
-    :param args: command-line arguments to pass to the script
-    :param dirs:
-    :param filter\_: function to filter out messages from the executable stdout
-    """
-    if args:
-        if isinstance(args, basestring):
-            args = shlex.split(args)
-    else:
-        args = []
-
-    if dir_:
-        def resolve(*args):
-            return ctxt.resolve(dir_, *args)
-    else:
-        resolve = ctxt.resolve
-
-    if file_ and os.path.isfile(resolve(file_)):
-        file_ = resolve(file_)
-
-    if executable is None:
-        executable = file_
-    elif file_:
-        args[:0] = [file_]
-
-    if input_:
-        input_file = file(resolve(input_), 'r')
-    else:
-        input_file = None
-
-    if output:
-        output_file = file(resolve(output), 'w')
-    else:
-        output_file = None
-
-    if dir_ and os.path.isdir(ctxt.resolve(dir_)):
-        dir_ = ctxt.resolve(dir_)
-    else:
-        dir_ = ctxt.basedir
-        
-    if not filter_:
-        filter_=lambda s: s
-
-    try:
-        cmdline = CommandLine(executable, args, input=input_file,
-                              cwd=dir_)
-        log_elem = xmlio.Fragment()
-        for out, err in cmdline.execute():
-            if out is not None:
-                log.info(out)
-                info = filter_(out)
-                if info:
-                    log_elem.append(xmlio.Element('message', level='info')[
-                        info.replace(ctxt.basedir + os.sep, '')
-                            .replace(ctxt.basedir, '')
-                ])
-                if output:
-                    output_file.write(out + os.linesep)
-            if err is not None:
-                log.error(err)
-                log_elem.append(xmlio.Element('message', level='error')[
-                    err.replace(ctxt.basedir + os.sep, '')
-                       .replace(ctxt.basedir, '')
-                ])
-                if output:
-                    output_file.write(err + os.linesep)
-        ctxt.log(log_elem)
-    finally:
-        if input_:
-            input_file.close()
-        if output:
-            output_file.close()
-
-    return cmdline.returncode
deleted file mode 100644
--- a/trac-0.11/bitten/build/svntools.py
+++ /dev/null
@@ -1,87 +0,0 @@
-# -*- coding: utf-8 -*-
-#
-# Copyright (C) 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.
-
-"""Recipe commands for Subversion."""
-
-import logging
-import posixpath
-import re
-
-log = logging.getLogger('bitten.build.svntools')
-
-__docformat__ = 'restructuredtext en'
-
-def checkout(ctxt, url, path=None, revision=None, dir_='.', verbose=False):
-    """Perform a checkout from a Subversion repository.
-    
-    :param ctxt: the build context
-    :type ctxt: `Context`
-    :param url: the URL of the repository
-    :param path: the path inside the repository
-    :param revision: the revision to check out
-    :param dir_: the name of a local subdirectory to check out into
-    :param verbose: whether to log the list of checked out files
-    """
-    args = ['checkout']
-    if revision:
-        args += ['-r', revision]
-    if path:
-        url = posixpath.join(url, path.lstrip('/'))
-    args += [url, dir_]
-
-    cofilter = None
-    if not verbose:
-        cre = re.compile(r'^[AU]\s.*$')
-        cofilter = lambda s: cre.sub('', s)
-    from bitten.build import shtools
-    returncode = shtools.execute(ctxt, file_='svn', args=args, 
-                                 filter_=cofilter)
-    if returncode != 0:
-        ctxt.error('svn checkout failed (%s)' % returncode)
-
-def export(ctxt, url, path=None, revision=None, dir_='.'):
-    """Perform an export from a Subversion repository.
-    
-    :param ctxt: the build context
-    :type ctxt: `Context`
-    :param url: the URL of the repository
-    :param path: the path inside the repository
-    :param revision: the revision to check out
-    :param dir_: the name of a local subdirectory to export out into
-    """
-    args = ['export', '--force']
-    if revision:
-        args += ['-r', revision]
-    if path:
-        url = posixpath.join(url, path)
-    args += [url, dir_]
-
-    from bitten.build import shtools
-    returncode = shtools.execute(ctxt, file_='svn', args=args)
-    if returncode != 0:
-        ctxt.error('svn export failed (%s)' % returncode)
-
-def update(ctxt, revision=None, dir_='.'):
-    """Update the local working copy from the Subversion repository.
-    
-    :param ctxt: the build context
-    :type ctxt: `Context`
-    :param revision: the revision to check out
-    :param dir_: the name of a local subdirectory containing the working copy
-    """
-    args = ['update']
-    if revision:
-        args += ['-r', revision]
-    args += [dir_]
-
-    from bitten.build import shtools
-    returncode = shtools.execute(ctxt, file_='svn', args=args)
-    if returncode != 0:
-        ctxt.error('svn update failed (%s)' % returncode)
deleted file mode 100644
--- a/trac-0.11/bitten/build/tests/__init__.py
+++ /dev/null
@@ -1,27 +0,0 @@
-# -*- 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 bitten.build.tests import api, config, ctools, phptools, pythontools, \
-                               xmltools
-
-def suite():
-    suite = unittest.TestSuite()
-    suite.addTest(api.suite())
-    suite.addTest(config.suite())
-    suite.addTest(ctools.suite())
-    suite.addTest(phptools.suite())
-    suite.addTest(pythontools.suite())
-    suite.addTest(xmltools.suite())
-    return suite
-
-if __name__ == '__main__':
-    unittest.main(defaultTest='suite')
deleted file mode 100644
--- a/trac-0.11/bitten/build/tests/api.py
+++ /dev/null
@@ -1,234 +0,0 @@
-# -*- 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 os
-import shutil
-import sys
-import tempfile
-import unittest
-
-from bitten.build import CommandLine, FileSet, TimeoutError
-from bitten.build.api import _combine
-
-
-class CommandLineTestCase(unittest.TestCase):
-
-    def setUp(self):
-        self.basedir = os.path.realpath(tempfile.mkdtemp(suffix='bitten_test'))
-
-    def tearDown(self):
-        shutil.rmtree(self.basedir)
-
-    def _create_file(self, name, content=None):
-        filename = os.path.join(self.basedir, name)
-        fd = file(filename, 'w')
-        if content:
-            fd.write(content)
-        fd.close()
-        return filename
-
-    def test_extract_lines(self):
-        cmdline = CommandLine('test', [])
-        data = ['foo\n', 'bar\n']
-        lines = cmdline._extract_lines(data)
-        self.assertEqual(['foo', 'bar'], lines)
-        self.assertEqual([], data)
-
-    def test_extract_lines_spanned(self):
-        cmdline = CommandLine('test', [])
-        data = ['foo ', 'bar\n']
-        lines = cmdline._extract_lines(data)
-        self.assertEqual(['foo bar'], lines)
-        self.assertEqual([], data)
-
-    def test_extract_lines_trailing(self):
-        cmdline = CommandLine('test', [])
-        data = ['foo\n', 'bar']
-        lines = cmdline._extract_lines(data)
-        self.assertEqual(['foo'], lines)
-        self.assertEqual(['bar'], data)
-
-    def test_combine(self):
-        list1 = ['foo', 'bar']
-        list2 = ['baz']
-        combined = list(_combine(list1, list2))
-        self.assertEqual([('foo', 'baz'), ('bar', None)], combined)
-
-    def test_single_argument(self):
-        cmdline = CommandLine(sys.executable, ['-V'])
-        stdout = []
-        stderr = []
-        for out, err in cmdline.execute(timeout=5.0):
-            if out is not None:
-                stdout.append(out)
-            if err is not None:
-                stderr.append(err)
-        py_version = '.'.join([str(v) for (v) in sys.version_info[:3]])
-        self.assertEqual(['Python %s' % py_version], stderr)
-        self.assertEqual([], stdout)
-        self.assertEqual(0, cmdline.returncode)
-
-    def test_multiple_arguments(self):
-        script_file = self._create_file('test.py', content="""
-import sys
-for arg in sys.argv[1:]:
-    print arg
-""")
-        cmdline = CommandLine('python', [script_file, 'foo', 'bar', 'baz'])
-        stdout = []
-        stderr = []
-        for out, err in cmdline.execute(timeout=5.0):
-            stdout.append(out)
-            stderr.append(err)
-        py_version = '.'.join([str(v) for (v) in sys.version_info[:3]])
-        self.assertEqual(['foo', 'bar', 'baz'], stdout)
-        self.assertEqual([None, None, None], stderr)
-        self.assertEqual(0, cmdline.returncode)
-
-    def test_output_error_streams(self):
-        script_file = self._create_file('test.py', content="""
-import sys, time
-print>>sys.stdout, 'Hello'
-print>>sys.stdout, 'world!'
-sys.stdout.flush()
-time.sleep(.1)
-print>>sys.stderr, 'Oops'
-sys.stderr.flush()
-""")
-        cmdline = CommandLine('python', [script_file])
-        stdout = []
-        stderr = []
-        for out, err in cmdline.execute(timeout=5.0):
-            stdout.append(out)
-            stderr.append(err)
-        py_version = '.'.join([str(v) for (v) in sys.version_info[:3]])
-        # nt doesn't properly split stderr and stdout. See ticket #256.
-        if os.name != "nt":
-            self.assertEqual(['Hello', 'world!', None], stdout)
-        self.assertEqual(0, cmdline.returncode)
-
-    def test_input_stream_as_fileobj(self):
-        script_file = self._create_file('test.py', content="""
-import sys
-data = sys.stdin.read()
-if data == 'abcd':
-    print>>sys.stdout, 'Thanks'
-""")
-        input_file = self._create_file('input.txt', content='abcd')
-        input_fileobj = file(input_file, 'r')
-        try:
-            cmdline = CommandLine('python', [script_file], input=input_fileobj)
-            stdout = []
-            stderr = []
-            for out, err in cmdline.execute(timeout=5.0):
-                stdout.append(out)
-                stderr.append(err)
-            py_version = '.'.join([str(v) for (v) in sys.version_info[:3]])
-            self.assertEqual(['Thanks'], stdout)
-            self.assertEqual([None], stderr)
-            self.assertEqual(0, cmdline.returncode)
-        finally:
-            input_fileobj.close()
-
-    def test_input_stream_as_string(self):
-        script_file = self._create_file('test.py', content="""
-import sys
-data = sys.stdin.read()
-if data == 'abcd':
-    print>>sys.stdout, 'Thanks'
-""")
-        cmdline = CommandLine('python', [script_file], input='abcd')
-        stdout = []
-        stderr = []
-        for out, err in cmdline.execute(timeout=5.0):
-            stdout.append(out)
-            stderr.append(err)
-        py_version = '.'.join([str(v) for (v) in sys.version_info[:3]])
-        self.assertEqual(['Thanks'], stdout)
-        self.assertEqual([None], stderr)
-        self.assertEqual(0, cmdline.returncode)
-
-    def test_timeout(self):
-        script_file = self._create_file('test.py', content="""
-import time
-time.sleep(2.0)
-print 'Done'
-""")
-        cmdline = CommandLine('python', [script_file])
-        iterable = iter(cmdline.execute(timeout=.5))
-        if os.name != "nt":
-            # commandline timeout not implemented on windows. See #257
-            self.assertRaises(TimeoutError, iterable.next)
-
-class FileSetTestCase(unittest.TestCase):
-
-    def setUp(self):
-        self.basedir = os.path.realpath(tempfile.mkdtemp(suffix='bitten_test'))
-
-    def tearDown(self):
-        shutil.rmtree(self.basedir)
-
-    # Convenience methods
-
-    def _create_dir(self, *path):
-        cur = self.basedir
-        for part in path:
-            cur = os.path.join(cur, part)
-            os.mkdir(cur)
-        return cur[len(self.basedir) + 1:]
-
-    def _create_file(self, *path):
-        filename = os.path.join(self.basedir, *path)
-        fd = file(filename, 'w')
-        fd.close()
-        return filename[len(self.basedir) + 1:]
-
-    # Test methods
-
-    def test_empty(self):
-        fileset = FileSet(self.basedir)
-        self.assertRaises(StopIteration, iter(fileset).next)
-
-    def test_top_level_files(self):
-        foo_txt = self._create_file('foo.txt')
-        bar_txt = self._create_file('bar.txt')
-        fileset = FileSet(self.basedir)
-        assert foo_txt in fileset and bar_txt in fileset
-
-    def test_files_in_subdir(self):
-        self._create_dir('tests')
-        foo_txt = self._create_file('tests', 'foo.txt')
-        bar_txt = self._create_file('tests', 'bar.txt')
-        fileset = FileSet(self.basedir)
-        assert foo_txt in fileset and bar_txt in fileset
-
-    def test_files_in_subdir_with_include(self):
-        self._create_dir('tests')
-        foo_txt = self._create_file('tests', 'foo.txt')
-        bar_txt = self._create_file('tests', 'bar.txt')
-        fileset = FileSet(self.basedir, include='tests/*.txt')
-        assert foo_txt in fileset and bar_txt in fileset
-
-    def test_files_in_subdir_with_exclude(self):
-        self._create_dir('tests')
-        foo_txt = self._create_file('tests', 'foo.txt')
-        bar_txt = self._create_file('tests', 'bar.txt')
-        fileset = FileSet(self.basedir, include='tests/*.txt', exclude='bar.*')
-        assert foo_txt in fileset and bar_txt not in fileset
-
-
-def suite():
-    suite = unittest.TestSuite()
-    suite.addTest(unittest.makeSuite(CommandLineTestCase, 'test'))
-    suite.addTest(unittest.makeSuite(FileSetTestCase, 'test'))
-    return suite
-
-if __name__ == '__main__':
-    unittest.main(defaultTest='suite')
deleted file mode 100644
--- a/trac-0.11/bitten/build/tests/config.py
+++ /dev/null
@@ -1,154 +0,0 @@
-# -*- 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 platform
-import os
-import tempfile
-import unittest
-
-from bitten.build.config import Configuration
-
-
-class ConfigurationTestCase(unittest.TestCase):
-
-    def test_sysinfo_defaults(self):
-        config = Configuration()
-
-        self.assertEqual(platform.machine(), config['machine'])
-        self.assertEqual(platform.processor(), config['processor'])
-        system, release, version = platform.system_alias(platform.system(),
-                                                         platform.release(),
-                                                         platform.version())
-        self.assertEqual(system, config['os'])
-        self.assertEqual(os.name, config['family'])
-        self.assertEqual(release, config['version'])
-
-    def test_sysinfo_properties_override(self):
-        config = Configuration(properties={
-            'machine': 'MACHINE',
-            'processor': 'PROCESSOR',
-            'os': 'OS',
-            'family': 'FAMILY',
-            'version': 'VERSION'
-        })
-        self.assertEqual('MACHINE', config['machine'])
-        self.assertEqual('PROCESSOR', config['processor'])
-        self.assertEqual('OS', config['os'])
-        self.assertEqual('FAMILY', config['family'])
-        self.assertEqual('VERSION', config['version'])
-
-    def test_sysinfo_configfile_override(self):
-        inifd, ininame = tempfile.mkstemp(prefix='bitten_test')
-        try:
-            os.write(inifd, """
-[machine]
-name = MACHINE
-processor = PROCESSOR
-
-[os]
-name = OS
-family = FAMILY
-version = VERSION
-""")
-            os.close(inifd)
-            config = Configuration(ininame)
-
-            self.assertEqual('MACHINE', config['machine'])
-            self.assertEqual('PROCESSOR', config['processor'])
-            self.assertEqual('OS', config['os'])
-            self.assertEqual('FAMILY', config['family'])
-            self.assertEqual('VERSION', config['version'])
-        finally:
-            os.remove(ininame)
-
-    def test_package_properties(self):
-        config = Configuration(properties={
-            'python.version': '2.3.5',
-            'python.path': '/usr/local/bin/python2.3'
-        })
-        self.assertEqual(True, 'python' in config.packages)
-        self.assertEqual('/usr/local/bin/python2.3', config['python.path'])
-        self.assertEqual('2.3.5', config['python.version'])
-
-    def test_package_configfile(self):
-        inifd, ininame = tempfile.mkstemp(prefix='bitten_test')
-        try:
-            os.write(inifd, """
-[python]
-path = /usr/local/bin/python2.3
-version = 2.3.5
-""")
-            os.close(inifd)
-            config = Configuration(ininame)
-
-            self.assertEqual(True, 'python' in config.packages)
-            self.assertEqual('/usr/local/bin/python2.3', config['python.path'])
-            self.assertEqual('2.3.5', config['python.version'])
-        finally:
-            os.remove(ininame)
-
-    def test_get_dirpath_non_existant(self):
-        tempdir = tempfile.mkdtemp()
-        os.rmdir(tempdir)
-        config = Configuration(properties={'somepkg.home': tempdir})
-        self.assertEqual(None, config.get_dirpath('somepkg.home'))
-
-    def test_get_dirpath(self):
-        tempdir = tempfile.mkdtemp()
-        try:
-            config = Configuration(properties={'somepkg.home': tempdir})
-            self.assertEqual(tempdir, config.get_dirpath('somepkg.home'))
-        finally:
-            os.rmdir(tempdir)
-
-    def test_get_filepath_non_existant(self):
-        testfile, testname = tempfile.mkstemp(prefix='bitten_test')
-        os.close(testfile)
-        os.remove(testname)
-        config = Configuration(properties={'somepkg.path': testname})
-        self.assertEqual(None, config.get_filepath('somepkg.path'))
-
-    def test_get_filepath(self):
-        testfile = tempfile.NamedTemporaryFile(prefix='bitten_test')
-        config = Configuration(properties={'somepkg.path': testfile.name})
-        self.assertEqual(testfile.name, config.get_filepath('somepkg.path'))
-
-    def test_interpolate(self):
-        config = Configuration(properties={
-            'python.version': '2.3.5',
-            'python.path': '/usr/local/bin/python2.3'
-        })
-        self.assertEqual('/usr/local/bin/python2.3',
-                         config.interpolate('${python.path}'))
-        self.assertEqual('foo /usr/local/bin/python2.3 bar',
-                         config.interpolate('foo ${python.path} bar'))
-
-    def test_interpolate_default(self):
-        config = Configuration()
-        self.assertEqual('python2.3',
-                         config.interpolate('${python.path:python2.3}'))
-        self.assertEqual('foo python2.3 bar',
-                         config.interpolate('foo ${python.path:python2.3} bar'))
-
-    def test_interpolate_missing(self):
-        config = Configuration()
-        self.assertEqual('${python.path}',
-                         config.interpolate('${python.path}'))
-        self.assertEqual('foo ${python.path} bar',
-                         config.interpolate('foo ${python.path} bar'))
-
-
-def suite():
-    suite = unittest.TestSuite()
-    suite.addTest(unittest.makeSuite(ConfigurationTestCase, 'test'))
-    return suite
-
-if __name__ == '__main__':
-    unittest.main(defaultTest='suite')
deleted file mode 100644
--- a/trac-0.11/bitten/build/tests/ctools.py
+++ /dev/null
@@ -1,156 +0,0 @@
-# -*- 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 os
-import shutil
-import tempfile
-import unittest
-
-from bitten.build import ctools
-from bitten.build.tests import dummy
-from bitten.recipe import Context, Recipe
-
-
-class CppUnitTestCase(unittest.TestCase):
-
-    def setUp(self):
-        self.basedir = os.path.realpath(tempfile.mkdtemp())
-        self.ctxt = Context(self.basedir)
-
-    def tearDown(self):
-        shutil.rmtree(self.basedir)
-
-    def test_missing_param_file(self):
-        self.assertRaises(AssertionError, ctools.cppunit, self.ctxt)
-
-    def test_empty_summary(self):
-        cppunit_xml = file(self.ctxt.resolve('cppunit.xml'), 'w')
-        cppunit_xml.write("""<?xml version="1.0" encoding='utf-8' ?>
-<TestRun>
-  <FailedTests>
-    <FailedTest id="2">
-      <Name>HelloTest::secondTest</Name>
-      <FailureType>Assertion</FailureType>
-      <Location>
-        <File>HelloTest.cxx</File>
-        <Line>95</Line>
-      </Location>
-      <Message>assertion failed
-- Expression: 2 == 3
-</Message>
-    </FailedTest>
-  </FailedTests>
-  <SuccessfulTests>
-    <Test id="1">
-      <Name>HelloTest::firstTest</Name>
-    </Test>
-    <Test id="3">
-      <Name>HelloTest::thirdTest</Name>
-    </Test>
-  </SuccessfulTests>
-  <Statistics>
-    <Tests>3</Tests>
-    <FailuresTotal>1</FailuresTotal>
-    <Errors>0</Errors>
-    <Failures>1</Failures>
-  </Statistics>
-</TestRun>""")
-        cppunit_xml.close()
-        ctools.cppunit(self.ctxt, file_='cppunit.xml')
-        type, category, generator, xml = self.ctxt.output.pop()
-        self.assertEqual(Recipe.REPORT, type)
-        self.assertEqual('test', category)
-
-        tests = list(xml.children)
-        self.assertEqual(3, len(tests))
-        self.assertEqual('HelloTest', tests[0].attr['fixture'])
-        self.assertEqual('secondTest', tests[0].attr['name'])
-        self.assertEqual('failure', tests[0].attr['status'])
-        self.assertEqual('HelloTest.cxx', tests[0].attr['file'])
-        self.assertEqual('95', tests[0].attr['line'])
-
-        self.assertEqual('HelloTest', tests[1].attr['fixture'])
-        self.assertEqual('firstTest', tests[1].attr['name'])
-        self.assertEqual('success', tests[1].attr['status'])
-
-        self.assertEqual('HelloTest', tests[2].attr['fixture'])
-        self.assertEqual('thirdTest', tests[2].attr['name'])
-        self.assertEqual('success', tests[2].attr['status'])
-
-
-class GCovTestCase(unittest.TestCase):
-
-    def setUp(self):
-        self.basedir = os.path.realpath(tempfile.mkdtemp())
-        self.ctxt = Context(self.basedir)
-
-    def tearDown(self):
-        shutil.rmtree(self.basedir)
-
-    def _create_file(self, *path):
-        filename = os.path.join(self.basedir, *path)
-        dirname = os.path.dirname(filename)
-        if not os.path.isdir(dirname):
-            os.makedirs(dirname)
-        fd = file(filename, 'w')
-        fd.close()
-        return filename[len(self.basedir) + 1:]
-
-    def test_no_file(self):
-        ctools.CommandLine = dummy.CommandLine()
-        ctools.gcov(self.ctxt)
-        type, category, generator, xml = self.ctxt.output.pop()
-        self.assertEqual('log', type)
-        type, category, generator, xml = self.ctxt.output.pop()
-        self.assertEqual('report', type)
-        self.assertEqual('coverage', category)
-        self.assertEqual(0, len(xml.children))
-
-    def test_single_file(self):
-        self._create_file('foo.c')
-        self._create_file('foo.o')
-        self._create_file('foo.gcno')
-        self._create_file('foo.gcda')
-
-        ctools.CommandLine = dummy.CommandLine(stdout="""
-File `foo.c'
-Lines executed:45.81% of 884
-Branches executed:54.27% of 398
-Taken at least once:36.68% of 398
-Calls executed:48.19% of 249
-
-File `foo.h'
-Lines executed:50.00% of 4
-No branches
-Calls executed:100.00% of 1
-""")
-        ctools.gcov(self.ctxt)
-        type, category, generator, xml = self.ctxt.output.pop()
-        self.assertEqual('log', type)
-        type, category, generator, xml = self.ctxt.output.pop()
-        self.assertEqual('report', type)
-        self.assertEqual('coverage', category)
-        self.assertEqual(1, len(xml.children))
-        elem = xml.children[0]
-        self.assertEqual('coverage', elem.name)
-        self.assertEqual('foo.c', elem.attr['file'])
-        self.assertEqual('foo.c', elem.attr['name'])
-        self.assertEqual(888, elem.attr['lines'])
-        self.assertEqual(45, elem.attr['percentage'])
-
-
-def suite():
-    suite = unittest.TestSuite()
-    suite.addTest(unittest.makeSuite(CppUnitTestCase, 'test'))
-    suite.addTest(unittest.makeSuite(GCovTestCase, 'test'))
-    return suite
-
-if __name__ == '__main__':
-    unittest.main(defaultTest='suite')
deleted file mode 100644
--- a/trac-0.11/bitten/build/tests/dummy.py
+++ /dev/null
@@ -1,27 +0,0 @@
-# -*- 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.
-
-from StringIO import StringIO
-
-from bitten.build import api
-
-
-class CommandLine(api.CommandLine):
-
-    def __init__(self, returncode=0, stdout='', stderr=''):
-        self.returncode = returncode
-        self.stdout = StringIO(stdout)
-        self.stderr = StringIO(stderr)
-
-    def __call__(self, executable, args, input=None, cwd=None):
-        return self
-
-    def execute(self):
-        return api._combine(self.stdout.readlines(), self.stderr.readlines())
deleted file mode 100644
--- a/trac-0.11/bitten/build/tests/javatools.py
+++ /dev/null
@@ -1,129 +0,0 @@
-# -*- coding: utf-8 -*-
-#
-# Copyright (C) 2005-2007 Christopher Lenz <cmlenz@gmx.de>
-# Copyright (C) 2006 Matthew Good <matt@matt-good.net>
-# 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 os.path
-import shutil
-import tempfile
-import unittest
-
-from bitten.build import javatools
-from bitten.recipe import Context
-
-class CoberturaTestCase(unittest.TestCase):
-    xml_template="""<?xml version="1.0"?>
-<!DOCTYPE coverage SYSTEM "http://cobertura.sourceforge.net/xml/coverage-02.dtd">
-
-<coverage timestamp="1148533713840">
-  <sources>
-    <source>src</source>
-  </sources>
-  <packages>
-    <package name="test">
-      <classes>%s
-      </classes>
-    </package>
-  </packages>
-</coverage>"""
-
-    def setUp(self):
-        self.basedir = os.path.realpath(tempfile.mkdtemp())
-        self.ctxt = Context(self.basedir)
-
-    def tearDown(self):
-        shutil.rmtree(self.basedir)
-
-    def _create_file(self, *path, **kw):
-        filename = os.path.join(self.basedir, *path)
-        dirname = os.path.dirname(filename)
-        if not os.path.exists(dirname):
-            os.makedirs(dirname)
-        fd = file(filename, 'w')
-        content = kw.get('content')
-        if content is not None:
-            fd.write(content)
-        fd.close()
-        return filename[len(self.basedir) + 1:]
-
-    def test_basic(self):
-        filename = self._create_file('coverage.xml', content=self.xml_template % """
-        <class name="test.TestClass" filename="test/TestClass.java">
-          <lines>
-            <line number="1" hits="0" branch="false"/>
-            <line number="2" hits="1" branch="false"/>
-            <line number="3" hits="0" branch="false"/>
-            <line number="4" hits="2" branch="false"/>
-          </lines>
-        </class>""")
-        javatools.cobertura(self.ctxt, file_=filename)
-        type, category, generator, xml = self.ctxt.output.pop()
-        self.assertEqual('report', type)
-        self.assertEqual('coverage', category)
-        self.assertEqual(1, len(xml.children))
-
-        elem = xml.children[0]
-        self.assertEqual('coverage', elem.name)
-        self.assertEqual('src/test/TestClass.java', elem.attr['file'])
-        self.assertEqual('test.TestClass', elem.attr['name'])
-        self.assertEqual(4, elem.attr['lines'])
-        self.assertEqual(50, elem.attr['percentage'])
-
-    def test_skipped_lines(self):
-        filename = self._create_file('coverage.xml', content=self.xml_template % """
-        <class name="test.TestClass" filename="test/TestClass.java">
-          <lines>
-            <line number="1" hits="0" branch="false"/>
-            <line number="3" hits="1" branch="false"/>
-          </lines>
-        </class>""")
-        javatools.cobertura(self.ctxt, file_=filename)
-        type, category, generator, xml = self.ctxt.output.pop()
-        self.assertEqual('report', type)
-        self.assertEqual('coverage', category)
-        self.assertEqual(1, len(xml.children))
-
-        elem = xml.children[0]
-        self.assertEqual('coverage', elem.name)
-        self.assertEqual('src/test/TestClass.java', elem.attr['file'])
-        self.assertEqual('test.TestClass', elem.attr['name'])
-        self.assertEqual(2, elem.attr['lines'])
-        self.assertEqual(50, elem.attr['percentage'])
-
-        line_hits = elem.children[0]
-        self.assertEqual('line_hits', line_hits.name)
-        self.assertEqual('0 - 1', line_hits.children[0])
-
-    def test_interface(self):
-        filename = self._create_file('coverage.xml', content=self.xml_template % """
-        <class name="test.TestInterface" filename="test/TestInterface.java">
-          <lines>
-          </lines>
-        </class>""")
-        javatools.cobertura(self.ctxt, file_=filename)
-        type, category, generator, xml = self.ctxt.output.pop()
-        self.assertEqual('report', type)
-        self.assertEqual('coverage', category)
-        self.assertEqual(1, len(xml.children))
-
-        elem = xml.children[0]
-        self.assertEqual('coverage', elem.name)
-        self.assertEqual('src/test/TestInterface.java', elem.attr['file'])
-        self.assertEqual('test.TestInterface', elem.attr['name'])
-        self.assertEqual(0, elem.attr['lines'])
-        self.assertEqual(0, elem.attr['percentage'])
-
-
-def suite():
-    suite = unittest.TestSuite()
-    suite.addTest(unittest.makeSuite(CoberturaTestCase, 'test'))
-    return suite
-
-if __name__ == '__main__':
-    unittest.main(defaultTest='suite')
deleted file mode 100644
--- a/trac-0.11/bitten/build/tests/phptools.py
+++ /dev/null
@@ -1,131 +0,0 @@
-# -*- coding: UTF-8 -*-
-#
-# Copyright (C) 2007 Edgewall Software
-# Copyright (C) 2007 Wei Zhuo <weizhuo@gmail.com>
-# 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.cmlenz.net/wiki/License.
-
-import os
-import shutil
-import tempfile
-import unittest
-
-from bitten.build import phptools
-from bitten.recipe import Context, Recipe
-
-class PhpUnitTestCase(unittest.TestCase):
-
-    def setUp(self):
-        self.basedir = os.path.realpath(tempfile.mkdtemp())
-        self.ctxt = Context(self.basedir)
-
-    def tearDown(self):
-        shutil.rmtree(self.basedir)
-
-    def test_missing_param_file(self):
-        self.assertRaises(AssertionError, phptools.phpunit, self.ctxt)
-
-    def test_sample_unit_test_result(self):
-        phpunit_xml = file(self.ctxt.resolve('phpunit.xml'), 'w')
-        phpunit_xml.write("""<?xml version="1.0" encoding="UTF-8"?>
-<testsuites>
-  <testsuite name="FooTest" file="FooTest.php" tests="2" failures="1" errors="0" time="0.147397">
-    <testcase name="testBar" class="FooTest" time="0.122265">
-      <failure message="expected same: &lt;1&gt; was not: &lt;2&gt;" type="PHPUnit2_Framework_AssertionFailedError">
-      ...
-</failure>
-    </testcase>
-    <testcase name="testBar2" class="FooTest" time="0.025132"/>
-  </testsuite>
-  <testsuite name="BarTest" file="BarTest.php" tests="1" failures="0" errors="0" time="0.050713">
-    <testcase name="testFoo" class="BarTest" time="0.026046"/>
-  </testsuite>
-</testsuites>""")
-        phpunit_xml.close()
-        phptools.phpunit(self.ctxt, file_='phpunit.xml')
-        type, category, generator, xml = self.ctxt.output.pop()
-        self.assertEqual(Recipe.REPORT, type)
-        self.assertEqual('test', category)
-
-        tests = list(xml.children)
-        self.assertEqual(3, len(tests))
-        self.assertEqual('FooTest', tests[0].attr['fixture'])
-        self.assertEqual('testBar', tests[0].attr['name'])
-        self.assertEqual('failure', tests[0].attr['status'])
-        self.assert_('FooTest.php' in tests[0].attr['file'])
-
-        self.assertEqual('FooTest', tests[1].attr['fixture'])
-        self.assertEqual('testBar2', tests[1].attr['name'])
-        self.assertEqual('success', tests[1].attr['status'])
-
-        self.assertEqual('BarTest', tests[2].attr['fixture'])
-        self.assertEqual('testFoo', tests[2].attr['name'])
-        self.assertEqual('success', tests[2].attr['status'])
-        
-class PhpCodeCoverageTestCase(unittest.TestCase):
-
-    def setUp(self):
-        self.basedir = os.path.realpath(tempfile.mkdtemp())
-        self.ctxt = Context(self.basedir)
-
-    def tearDown(self):
-        shutil.rmtree(self.basedir)
-
-    def test_missing_param_file(self):
-        self.assertRaises(AssertionError, phptools.coverage, self.ctxt)
-
-    def test_sample_code_coverage(self):
-        coverage_xml = file(self.ctxt.resolve('phpcoverage.xml'), 'w')
-        coverage_xml.write("""<?xml version="1.0" encoding="UTF-8"?>
-<snapshot methodcount="4" methodscovered="2" statementcount="11" statementscovered="5" totalcount="15" totalcovered="7">
-  <package name="default" methodcount="4" methodscovered="2" statementcount="11" statementscovered="5" totalcount="15" totalcovered="7">
-    <class name="Foo" methodcount="1" methodscovered="1" statementcount="7" statementscovered="3" totalcount="8" totalcovered="4">
-      <sourcefile name="Foo.php" sourcefile="xxxx/Foo.php">
-	  ...
-      </sourcefile>
-    </class>
-    <class name="Foo2" methodcount="2" methodscovered="1" statementcount="4" statementscovered="2" totalcount="6" totalcovered="3">
-      <sourcefile name="Foo.php" sourcefile="xxxx/Foo.php">
-        ...
-      </sourcefile>
-    </class>
-    <class name="Bar" methodcount="1" methodscovered="0" statementcount="0" statementscovered="0" totalcount="1" totalcovered="0">
-      <sourcefile name="Bar.php" sourcefile="xxxx/Bar.php">
-        ...
-      </sourcefile>
-    </class>
-  </package>
-</snapshot>""")
-        coverage_xml.close()
-        phptools.coverage(self.ctxt, file_='phpcoverage.xml')
-        type, category, generator, xml = self.ctxt.output.pop()
-        self.assertEqual(Recipe.REPORT, type)
-        self.assertEqual('coverage', category)
-
-        coverage = list(xml.children)
-        self.assertEqual(3, len(coverage))
-        self.assertEqual(7, coverage[0].attr['lines'])
-        self.assertEqual('Foo', coverage[0].attr['name'])
-        self.assert_('xxxx/Foo.php' in coverage[0].attr['file'])
-
-        self.assertEqual(4, coverage[1].attr['lines'])
-        self.assertEqual(50.0, coverage[1].attr['percentage'])
-        self.assertEqual('Foo2', coverage[1].attr['name'])
-        self.assert_('xxxx/Foo.php' in coverage[1].attr['file'])
-        
-        self.assertEqual(0, coverage[2].attr['lines'])
-        self.assertEqual(100.0, coverage[2].attr['percentage'])
-        self.assertEqual('Bar', coverage[2].attr['name'])
-        self.assert_('xxxx/Bar.php' in coverage[2].attr['file'])
-
-def suite():
-    suite = unittest.TestSuite()
-    suite.addTest(unittest.makeSuite(PhpUnitTestCase, 'test'))
-    suite.addTest(unittest.makeSuite(PhpCodeCoverageTestCase, 'test'))
-    return suite
-
-if __name__ == '__main__':
-    unittest.main(defaultTest='suite')
deleted file mode 100644
--- a/trac-0.11/bitten/build/tests/pythontools.py
+++ /dev/null
@@ -1,413 +0,0 @@
-# -*- coding: utf-8 -*-
-#
-# Copyright (C) 2005-2007 Christopher Lenz <cmlenz@gmx.de>
-# Copyright (C) 2008 Matt Good <matt@matt-good.net>
-# Copyright (C) 2008 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 os
-import cPickle as pickle
-import shutil
-import tempfile
-import unittest
-
-from bitten.build import pythontools
-from bitten.build import FileSet
-from bitten.recipe import Context, Recipe
-
-
-class CoverageTestCase(unittest.TestCase):
-
-    def setUp(self):
-        self.basedir = os.path.realpath(tempfile.mkdtemp())
-        self.ctxt = Context(self.basedir)
-        self.summary = open(os.path.join(self.basedir, 'test-coverage.txt'),
-                            'w')
-        self.coverdir = os.path.join(self.basedir, 'coverage')
-        os.mkdir(self.coverdir)
-
-    def tearDown(self):
-        shutil.rmtree(self.basedir)
-
-    def _create_file(self, *path):
-        filename = os.path.join(self.basedir, *path)
-        dirname = os.path.dirname(filename)
-        os.makedirs(dirname)
-        fd = file(filename, 'w')
-        fd.close()
-        return filename[len(self.basedir) + 1:]
-
-    def test_missing_param_summary(self):
-        self.summary.close()
-        self.assertRaises(AssertionError, pythontools.coverage, self.ctxt,
-                          coverdir=self.coverdir)
-
-    def test_empty_summary(self):
-        self.summary.write("""
-Name         Stmts    Exec  Cover   Missing
--------------------------------------------
-""")
-        self.summary.close()
-        pythontools.coverage(self.ctxt, summary=self.summary.name, include='*.py')
-        type, category, generator, xml = self.ctxt.output.pop()
-        self.assertEqual(Recipe.REPORT, type)
-        self.assertEqual('coverage', category)
-        self.assertEqual(0, len(xml.children))
-
-    def test_summary_with_absolute_path(self):
-        self.summary.write("""
-Name         Stmts    Exec  Cover   Missing
--------------------------------------------
-test.module     60      60   100%% %s/test/module.py
-""" % self.ctxt.basedir)
-        self.summary.close()
-        self._create_file('test', 'module.py')
-        pythontools.coverage(self.ctxt, summary=self.summary.name,
-                             include='test/*')
-        type, category, generator, xml = self.ctxt.output.pop()
-        self.assertEqual(Recipe.REPORT, type)
-        self.assertEqual('coverage', category)
-        self.assertEqual(1, len(xml.children))
-        child = xml.children[0]
-        self.assertEqual('coverage', child.name)
-        self.assertEqual('test.module', child.attr['name'])
-        self.assertEqual('test/module.py', child.attr['file'])
-        self.assertEqual(100, child.attr['percentage'])
-        self.assertEqual(60, child.attr['lines'])
-
-    def test_summary_with_relative_path(self):
-        self.summary.write("""
-Name         Stmts    Exec  Cover   Missing
--------------------------------------------
-test.module     60      60   100% ./test/module.py
-""")
-        self.summary.close()
-        self._create_file('test', 'module.py')
-        pythontools.coverage(self.ctxt, summary=self.summary.name,
-                             include='test/*')
-        type, category, generator, xml = self.ctxt.output.pop()
-        self.assertEqual(Recipe.REPORT, type)
-        self.assertEqual('coverage', category)
-        self.assertEqual(1, len(xml.children))
-        child = xml.children[0]
-        self.assertEqual('coverage', child.name)
-        self.assertEqual('test.module', child.attr['name'])
-        self.assertEqual('test/module.py', child.attr['file'])
-        self.assertEqual(100, child.attr['percentage'])
-        self.assertEqual(60, child.attr['lines'])
-
-    def test_summary_with_missing_lines(self):
-        self.summary.write("""
-Name         Stmts   Exec  Cover   Missing
--------------------------------------------
-test.module     28     26    92%   13-14 ./test/module.py
-""")
-        self.summary.close()
-        self._create_file('test', 'module.py')
-        pythontools.coverage(self.ctxt, summary=self.summary.name,
-                             include='test/*')
-        type, category, generator, xml = self.ctxt.output.pop()
-        self.assertEqual(Recipe.REPORT, type)
-        self.assertEqual('coverage', category)
-        self.assertEqual(1, len(xml.children))
-        child = xml.children[0]
-        self.assertEqual('coverage', child.name)
-        self.assertEqual('test.module', child.attr['name'])
-        self.assertEqual('test/module.py', child.attr['file'])
-        self.assertEqual(92, child.attr['percentage'])
-        self.assertEqual(28, child.attr['lines'])
-
-
-class TraceTestCase(unittest.TestCase):
-
-    def setUp(self):
-        self.basedir = os.path.realpath(tempfile.mkdtemp())
-        self.ctxt = Context(self.basedir)
-        self.summary = open(os.path.join(self.basedir, 'test-coverage.txt'),
-                            'w')
-        self.coverdir = os.path.join(self.basedir, 'coverage')
-        os.mkdir(self.coverdir)
-
-    def tearDown(self):
-        shutil.rmtree(self.basedir)
-
-    def _create_file(self, *path):
-        filename = os.path.join(self.basedir, *path)
-        dirname = os.path.dirname(filename)
-        os.makedirs(dirname)
-        fd = file(filename, 'w')
-        fd.close()
-        return filename[len(self.basedir) + 1:]
-
-    def test_missing_param_summary(self):
-        self.summary.close()
-        self.assertRaises(AssertionError, pythontools.trace, self.ctxt,
-                          coverdir='coverage')
-
-    def test_missing_param_coverdir(self):
-        self.summary.close()
-        self.assertRaises(AssertionError, pythontools.trace, self.ctxt,
-                          summary='test-coverage.txt')
-
-    def test_empty_summary(self):
-        self.summary.write('line  cov%  module  (path)')
-        self.summary.close()
-        pythontools.trace(self.ctxt, summary=self.summary.name, include='*.py',
-                          coverdir=self.coverdir)
-        type, category, generator, xml = self.ctxt.output.pop()
-        self.assertEqual(Recipe.REPORT, type)
-        self.assertEqual('coverage', category)
-        self.assertEqual(0, len(xml.children))
-
-    def test_summary_with_absolute_path(self):
-        self.summary.write("""
-lines   cov%%   module   (path)
-   60   100%%   test.module   (%s/test/module.py)
-""" % self.ctxt.basedir)
-        self.summary.close()
-        self._create_file('test', 'module.py')
-        pythontools.trace(self.ctxt, summary=self.summary.name,
-                          include='test/*', coverdir=self.coverdir)
-        type, category, generator, xml = self.ctxt.output.pop()
-        self.assertEqual(Recipe.REPORT, type)
-        self.assertEqual('coverage', category)
-        self.assertEqual(1, len(xml.children))
-        child = xml.children[0]
-        self.assertEqual('coverage', child.name)
-        self.assertEqual('test.module', child.attr['name'])
-        self.assertEqual('test/module.py', child.attr['file'])
-
-    def test_summary_with_relative_path(self):
-        self.summary.write("""
-lines   cov%   module   (path)
-   60   100%   test.module   (./test/module.py)
-""")
-        self.summary.close()
-        self._create_file('test', 'module.py')
-        pythontools.trace(self.ctxt, summary=self.summary.name,
-                          include='test/*', coverdir=self.coverdir)
-        type, category, generator, xml = self.ctxt.output.pop()
-        self.assertEqual(Recipe.REPORT, type)
-        self.assertEqual('coverage', category)
-        self.assertEqual(1, len(xml.children))
-        child = xml.children[0]
-        self.assertEqual('coverage', child.name)
-        self.assertEqual('test.module', child.attr['name'])
-        self.assertEqual('test/module.py', child.attr['file'])
-
-
-class FigleafTestCase(unittest.TestCase):
-
-    def setUp(self):
-        self.basedir = os.path.realpath(tempfile.mkdtemp())
-        self.ctxt = Context(self.basedir)
-        self.summary = open(os.path.join(self.basedir, '.figleaf'), 'w')
-
-    def tearDown(self):
-        shutil.rmtree(self.basedir)
-
-    def _create_file(self, *path):
-        filename = os.path.join(self.basedir, *path)
-        dirname = os.path.dirname(filename)
-        os.makedirs(dirname)
-        fd = file(filename, 'w')
-        fd.close()
-        return filename[len(self.basedir) + 1:]
-
-    def test_missing_param_summary(self):
-        self.summary.close()
-        self.assertRaises(AssertionError, pythontools.coverage, self.ctxt)
-
-    def test_empty_summary(self):
-        pickle.dump({}, self.summary)
-        self.summary.close()
-        pythontools.figleaf(self.ctxt, summary=self.summary.name, include='*.py')
-        type, category, generator, xml = self.ctxt.output.pop()
-        self.assertEqual(Recipe.REPORT, type)
-        self.assertEqual('coverage', category)
-        self.assertEqual(0, len(xml.children))
-
-    def test_missing_coverage_file(self):
-        self.summary.close()
-        pythontools.figleaf(self.ctxt, summary='non-existant-file', include='*.py')
-        self.assertEqual([], self.ctxt.output)
-
-    def test_summary_with_absolute_path(self):
-        filename = os.sep.join([self.ctxt.basedir, 'test', 'module.py'])
-        pickle.dump({
-            filename: set([1, 4, 5]),
-        }, self.summary)
-        self.summary.close()
-        sourcefile = self.ctxt.resolve(self._create_file('test', 'module.py'))
-        open(sourcefile, 'w').write(
-            "if foo: # line 1\n"
-            "  print 'uncovered' # line 2\n"
-            "else: # line 3 (uninteresting)\n"
-            "  print 'covered' # line 4\n"
-            "print 'covered' # line 6\n"
-        )
-        pythontools.figleaf(self.ctxt, summary=self.summary.name,
-                            include='test/*')
-        type, category, generator, xml = self.ctxt.output.pop()
-        self.assertEqual(Recipe.REPORT, type)
-        self.assertEqual('coverage', category)
-        self.assertEqual(1, len(xml.children))
-        child = xml.children[0]
-        self.assertEqual('coverage', child.name)
-        self.assertEqual('test.module', child.attr['name'])
-        self.assertEqual(os.path.join('test', 'module.py'), child.attr['file'])
-        self.assertEqual(75, child.attr['percentage'])
-        self.assertEqual(4, child.attr['lines'])
-        self.assertEqual('1 0 - 1 1', child.attr['line_hits'])
-
-    def test_summary_with_non_covered_file(self):
-        pickle.dump({}, self.summary)
-        self.summary.close()
-        sourcefile = self.ctxt.resolve(self._create_file('test', 'module.py'))
-        open(sourcefile, 'w').write(
-            "print 'line 1'\n"
-            "print 'line 2'\n"
-            "print 'line 3'\n"
-            "print 'line 4'\n"
-            "print 'line 5'\n"
-        )
-        pythontools.figleaf(self.ctxt, summary=self.summary.name,
-                            include='test/*')
-        type, category, generator, xml = self.ctxt.output.pop()
-        self.assertEqual(Recipe.REPORT, type)
-        self.assertEqual('coverage', category)
-        self.assertEqual(1, len(xml.children))
-        child = xml.children[0]
-        self.assertEqual('coverage', child.name)
-        self.assertEqual('test.module', child.attr['name'])
-        self.assertEqual(os.path.join('test', 'module.py'), child.attr['file'])
-        self.assertEqual(0, child.attr['percentage'])
-        self.assertEqual(5, child.attr['lines'])
-
-    def test_summary_with_non_python_files(self):
-        "Figleaf coverage reports should not include files that do not end in .py"
-        pickle.dump({}, self.summary)
-        self.summary.close()
-        sourcefile = self.ctxt.resolve(self._create_file('test', 'document.txt'))
-        open(sourcefile, 'w').write("\n")
-        pythontools.figleaf(self.ctxt, summary=self.summary.name,
-                            include='test/*')
-        type, category, generator, xml = self.ctxt.output.pop()
-        self.assertEqual(Recipe.REPORT, type)
-        self.assertEqual('coverage', category)
-        self.assertEqual(0, len(xml.children))
-
-
-class FilenameNormalizationTestCase(unittest.TestCase):
-
-    def setUp(self):
-        self.basedir = os.path.realpath(tempfile.mkdtemp())
-        self.ctxt = Context(self.basedir)
-
-    def tearDown(self):
-        shutil.rmtree(self.basedir)
-
-    def _create_file(self, *path):
-        filename = os.path.join(self.basedir, *path)
-        dirname = os.path.dirname(filename)
-        os.makedirs(dirname)
-        fd = file(filename, 'w')
-        fd.close()
-        return filename[len(self.basedir) + 1:]
-
-    def test_absolute_path(self):
-        filename = os.sep.join([self.ctxt.basedir, 'test', 'module.py'])
-        self._create_file('test', 'module.py')
-        filenames = pythontools._normalize_filenames(
-                            self.ctxt, [filename],
-                            FileSet(self.ctxt.basedir, '**/*.py', None))
-        self.assertEqual(['test/module.py'], list(filenames))
-
-
-class UnittestTestCase(unittest.TestCase):
-
-    def setUp(self):
-        self.basedir = os.path.realpath(tempfile.mkdtemp())
-        self.ctxt = Context(self.basedir)
-        self.results_xml = open(os.path.join(self.basedir, 'test-results.xml'),
-                                'w')
-
-    def tearDown(self):
-        shutil.rmtree(self.basedir)
-
-    def test_missing_file_param(self):
-        self.results_xml.close()
-        self.assertRaises(AssertionError, pythontools.unittest, self.ctxt)
-
-    def test_empty_results(self):
-        self.results_xml.write('<?xml version="1.0"?>'
-                              '<unittest-results>'
-                              '</unittest-results>')
-        self.results_xml.close()
-        pythontools.unittest(self.ctxt, self.results_xml.name)
-        type, category, generator, xml = self.ctxt.output.pop()
-        self.assertEqual(Recipe.REPORT, type)
-        self.assertEqual('test', category)
-        self.assertEqual(0, len(xml.children))
-
-    def test_successful_test(self):
-        self.results_xml.write('<?xml version="1.0"?>'
-                              '<unittest-results>'
-                              '<test duration="0.12" status="success"'
-                              '      file="%s"'
-                              '      name="test_foo (pkg.BarTestCase)"/>'
-                              '</unittest-results>'
-                              % os.path.join(self.ctxt.basedir, 'bar_test.py'))
-        self.results_xml.close()
-        pythontools.unittest(self.ctxt, self.results_xml.name)
-        type, category, generator, xml = self.ctxt.output.pop()
-        self.assertEqual(1, len(xml.children))
-        test_elem = xml.children[0]
-        self.assertEqual('test', test_elem.name)
-        self.assertEqual('0.12', test_elem.attr['duration'])
-        self.assertEqual('success', test_elem.attr['status'])
-        self.assertEqual('bar_test.py', test_elem.attr['file'])
-        self.assertEqual('test_foo (pkg.BarTestCase)', test_elem.attr['name'])
-
-    def test_file_path_normalization(self):
-        self.results_xml.write('<?xml version="1.0"?>'
-                              '<unittest-results>'
-                              '<test duration="0.12" status="success"'
-                              '      file="%s"'
-                              '      name="test_foo (pkg.BarTestCase)"/>'
-                              '</unittest-results>'
-                              % os.path.join(self.ctxt.basedir, 'bar_test.py'))
-        self.results_xml.close()
-        pythontools.unittest(self.ctxt, self.results_xml.name)
-        type, category, generator, xml = self.ctxt.output.pop()
-        self.assertEqual(1, len(xml.children))
-        self.assertEqual('bar_test.py', xml.children[0].attr['file'])
-
-    def test_missing_file_attribute(self):
-        self.results_xml.write('<?xml version="1.0"?>'
-                              '<unittest-results>'
-                              '<test duration="0.12" status="success"'
-                              '      name="test_foo (pkg.BarTestCase)"/>'
-                              '</unittest-results>')
-        self.results_xml.close()
-        pythontools.unittest(self.ctxt, self.results_xml.name)
-        type, category, generator, xml = self.ctxt.output.pop()
-        self.assertEqual(1, len(xml.children))
-        self.assertEqual(None, xml.children[0].attr.get('file'))
-
-
-def suite():
-    suite = unittest.TestSuite()
-    suite.addTest(unittest.makeSuite(CoverageTestCase, 'test'))
-    suite.addTest(unittest.makeSuite(TraceTestCase, 'test'))
-    suite.addTest(unittest.makeSuite(FigleafTestCase, 'test'))
-    suite.addTest(unittest.makeSuite(FilenameNormalizationTestCase, 'test'))
-    suite.addTest(unittest.makeSuite(UnittestTestCase, 'test'))
-    return suite
-
-if __name__ == '__main__':
-    unittest.main(defaultTest='suite')
deleted file mode 100644
--- a/trac-0.11/bitten/build/tests/xmltools.py
+++ /dev/null
@@ -1,127 +0,0 @@
-# -*- 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 os
-import shutil
-import tempfile
-import unittest
-
-from bitten.build import xmltools
-from bitten.recipe import Context
-from bitten.util import xmlio
-
-
-class TransformTestCase(unittest.TestCase):
-
-    def setUp(self):
-        self.basedir = os.path.realpath(tempfile.mkdtemp())
-        self.ctxt = Context(self.basedir)
-
-    def tearDown(self):
-        shutil.rmtree(self.basedir)
-
-    def test_transform_no_src(self):
-        self.assertRaises(AssertionError, xmltools.transform, self.ctxt)
-
-    def test_transform_no_dest(self):
-        self.assertRaises(AssertionError, xmltools.transform, self.ctxt,
-                          src='src.xml')
-
-    def test_transform_no_stylesheet(self):
-        self.assertRaises(AssertionError, xmltools.transform, self.ctxt,
-                          src='src.xml', dest='dest.xml')
-
-    def test_transform(self):
-        src_file = file(self.ctxt.resolve('src.xml'), 'w')
-        try:
-            src_file.write("""<doc>
-<title>Document Title</title>
-<section>
-<title>Section Title</title>
-<para>This is a test.</para>
-<note>This is a note.</note>
-</section>
-</doc>
-""")
-        finally:
-            src_file.close()
-
-        style_file = file(self.ctxt.resolve('style.xsl'), 'w')
-        try:
-            style_file.write("""<xsl:stylesheet version="1.0"
-    xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
-    xmlns="http://www.w3.org/TR/xhtml1/strict">
- <xsl:template match="doc">
-  <html>
-   <head>
-    <title><xsl:value-of select="title"/></title>
-   </head>
-   <body>
-    <xsl:apply-templates/>
-   </body>
-  </html>
- </xsl:template>
- <xsl:template match="doc/title">
-  <h1><xsl:apply-templates/></h1>
- </xsl:template>
- <xsl:template match="section/title">
-  <h2><xsl:apply-templates/></h2>
- </xsl:template>
- <xsl:template match="para">
-  <p><xsl:apply-templates/></p>
- </xsl:template>
- <xsl:template match="note">
-  <p class="note"><b>NOTE: </b><xsl:apply-templates/></p>
- </xsl:template>
-</xsl:stylesheet>
-""")
-        finally:
-            style_file.close()
-
-        xmltools.transform(self.ctxt, src='src.xml', dest='dest.xml',
-                           stylesheet='style.xsl')
-
-        dest_file = file(self.ctxt.resolve('dest.xml'))
-        try:
-            dest = xmlio.parse(dest_file)
-        finally:
-            dest_file.close()
-
-        self.assertEqual('html', dest.name)
-        self.assertEqual('http://www.w3.org/TR/xhtml1/strict', dest.namespace)
-        children = list(dest.children())
-        self.assertEqual(2, len(children))
-        self.assertEqual('head', children[0].name)
-        head_children = list(children[0].children())
-        self.assertEqual(1, len(head_children))
-        self.assertEqual('title', head_children[0].name)
-        self.assertEqual('Document Title', head_children[0].gettext())
-        self.assertEqual('body', children[1].name)
-        body_children = list(children[1].children())
-        self.assertEqual(4, len(body_children))
-        self.assertEqual('h1', body_children[0].name)
-        self.assertEqual('Document Title', body_children[0].gettext())
-        self.assertEqual('h2', body_children[1].name)
-        self.assertEqual('Section Title', body_children[1].gettext())
-        self.assertEqual('p', body_children[2].name)
-        self.assertEqual('This is a test.', body_children[2].gettext())
-        self.assertEqual('p', body_children[3].name)
-        self.assertEqual('note', body_children[3].attr['class'])
-        self.assertEqual('This is a note.', body_children[3].gettext())
-
-
-def suite():
-    suite = unittest.TestSuite()
-    if xmltools.have_libxslt or xmltools.have_msxml:
-        suite.addTest(unittest.makeSuite(TransformTestCase, 'test'))
-    return suite
-
-if __name__ == '__main__':
-    unittest.main(defaultTest='suite')
deleted file mode 100644
--- a/trac-0.11/bitten/build/xmltools.py
+++ /dev/null
@@ -1,98 +0,0 @@
-# -*- 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.
-
-"""Recipe commands for XML processing."""
-
-import logging
-import os
-
-try:
-    import libxml2
-    import libxslt
-    have_libxslt = True
-except ImportError:
-    have_libxslt = False
-
-if not have_libxslt and os.name == 'nt':
-    try:
-        import win32com.client
-        have_msxml = True
-    except ImportError:
-        have_msxml = False
-else:
-    have_msxml = False
-
-log = logging.getLogger('bitten.build.xmltools')
-
-__docformat__ = 'restructuredtext en'
-
-def transform(ctxt, src=None, dest=None, stylesheet=None):
-    """Apply an XSLT stylesheet to a source XML document.
-    
-    This command requires either libxslt (with Python bindings), or MSXML to
-    be installed.
-    
-    :param ctxt: the build context
-    :type ctxt: `Context`
-    :param src: name of the XML input file
-    :param dest: name of the XML output file
-    :param stylesheet: name of the file containing the XSLT stylesheet
-    """
-    assert src, 'Missing required attribute "src"'
-    assert dest, 'Missing required attribute "dest"'
-    assert stylesheet, 'Missing required attribute "stylesheet"'
-
-    if have_libxslt:
-        log.debug('Using libxslt for XSLT transformation')
-        srcdoc, styledoc, result = None, None, None
-        try:
-            srcdoc = libxml2.parseFile(ctxt.resolve(src))
-            styledoc = libxslt.parseStylesheetFile(ctxt.resolve(stylesheet))
-            result = styledoc.applyStylesheet(srcdoc, None)
-            styledoc.saveResultToFilename(ctxt.resolve(dest), result, 0)
-        finally:
-            if styledoc:
-                styledoc.freeStylesheet()
-            if srcdoc:
-                srcdoc.freeDoc()
-            if result:
-                result.freeDoc()
-
-    elif have_msxml:
-        log.debug('Using MSXML for XSLT transformation')
-        srcdoc = win32com.client.Dispatch('MSXML2.DOMDocument.3.0')
-        if not srcdoc.load(ctxt.resolve(src)):
-            err = styledoc.parseError
-            ctxt.error('Failed to parse XML source %s: %s', src, err.reason)
-            return
-        styledoc = win32com.client.Dispatch('MSXML2.DOMDocument.3.0')
-        if not styledoc.load(ctxt.resolve(stylesheet)):
-            err = styledoc.parseError
-            ctxt.error('Failed to parse XSLT stylesheet %s: %s', stylesheet,
-                       err.reason)
-            return
-        result = srcdoc.transformNode(styledoc)
-
-        # MSXML seems to always write produce the resulting XML document using
-        # UTF-16 encoding, regardless of the encoding specified in the
-        # stylesheet. For better interoperability, recode to UTF-8 here.
-        result = result.encode('utf-8').replace(' encoding="UTF-16"?>', '?>')
-
-        dest_file = file(ctxt.resolve(dest), 'w')
-        try:
-            dest_file.write(result)
-        finally:
-            dest_file.close()
-
-    else:
-        ctxt.error('No usable XSLT implementation found')
-
-        # TODO: as a last resort, try to invoke 'xsltproc' to do the
-        #       transformation?
deleted file mode 100644
--- a/trac-0.11/bitten/htdocs/admin.css
+++ /dev/null
@@ -1,6 +0,0 @@
-table.form th { text-align: right; }
-div.platforms h3 { margin-top: 3em; }
-table#platformlist td ul { list-style: none; margin: 0; padding: 0; }
-
-dl.help { color: #666; font-size: 90%; margin: 1em .5em; }
-dl.help dt { font-weight: bold; }
deleted file mode 100644
--- a/trac-0.11/bitten/htdocs/bitten.css
+++ /dev/null
@@ -1,183 +0,0 @@
-/* Timeline styles */
-#content.timeline dt.successbuild, #content.timeline dt.successbuild a {
-  background-image: url(bitten_build.png) !important;
-}
-#content.timeline dt.failedbuild, #content.timeline dt.failedbuild a {
-  background-image: url(bitten_buildf.png) !important;
-}
-
-#content.build h2.config, #content.build h2.step { background: #f7f7f7;
-  border-bottom: 1px solid #d7d7d7; margin: 2em 0 0;
-}
-#content.build h2.config :link, #content.build h2.config :visited {
-  color: #b00; display: block; border-bottom: none;
-}
-#content.build h2.deactivated { text-decoration: line-through; }
-#content.build #prefs { line-height: 1.4em; }
-
-#content.build h3.builds { font-weight: bold; text-align: left;
-  margin: 2em 0 0 2em;
-}
-#content.build table.builds { border-collapse: separate;
-  border-top: 1px solid #666; margin-left: 2em; table-layout: fixed;
-}
-#content.build table.builds th { padding: 0 1em 0 .25em; text-align: left;
-  vertical-align: top;
-}
-#content.build table.builds th p { color: #666; font-size: smaller;
-  margin-top: 0;
-}
-#content.build table.builds th p.message { font-style: italic; }
-#content.build table.builds td { color: #999; border: 1px solid;
-  padding: .25em .5em; vertical-align: top;
-}
-#content.build table.builds td :link, #content.build table.builds td :visited {
-  font-weight: bold;
-}
-#content.build table.builds td.completed { background: #9d9; border-color: #696;
-  color: #393;
-}
-#content.build table.builds td.failed { background: #d99; border-color: #966;
-  color: #933;
-}
-#content.build table.builds td.in-progress { background: #dd9;
-  border-color: #996; color: #993;
-}
-#content.build table.builds td p { font-size: smaller; margin-top: 0; }
-#content.build table.builds .status { color: #000; }
-#content.build table.builds .system { font-size: smaller; line-height: 1.2em;
-  margin: .5em 0;
-}
-
-#content.build form.config { margin-top: 1em; }
-#content.build form.config th { text-align: left; }
-#content.build form.config fieldset { margin-bottom: 1em; }
-#content.build div.platforms { margin-top: 2em; }
-#content.build form.platforms ul { list-style-type: none; padding-left: 1em; }
-
-#content.build p.path { color: #999; font-size: smaller; margin-top: 0; }
-
-#content.build #charts { clear: right; float: right; width: 44%; }
-
-#content.build #builds { clear: none; margin-top: 2em; table-layout: fixed;
-  width: 54%;
-}
-#content.build #builds tbody th, #content.build #builds tbody td {
-  background: #fff;
-}
-#content.build #builds th.chgset { width: 6em; }
-#content.build #builds td :link, #content.build #builds td :visited {
-  font-weight: bold;
-}
-#content.build #builds tbody td { background-position: 2px .5em;
-  background-repeat: no-repeat;
-}
-#content.build #builds td.completed {
-  background-color: #e8f6e8; background-image: url(bitten_build.png);
-}
-#content.build #builds td.failed {
-  background-color: #fbe8e7; background-image: url(bitten_buildf.png);
-}
-#content.build #builds td.in-progress {
-  background-color: #f6fae0; background-image: url(bitten_build.png);
-}
-#content.build #builds .info { margin-left: 16px; }
-#content.build #builds :link, #content.build #builds :visited {
-  text-decoration: none;
-}
-#content.build #builds .info .status { color: #000; }
-#content.build #builds .info .system { color: #999; font-size: smaller;
-  line-height: 1.2em; margin-top: .5em;
-}
-#content.build #builds ul.steps {
-  list-style-type: none; margin: .5em 0 0; padding: 0;
-}
-#content.build #builds ul.steps li.success,
-#content.build #builds ul.steps li.failed {
-  border: 1px solid; margin: 1px 0; padding: 0 2px 0 12px;
-}
-#content.build #builds ul.steps li.success {
-  background: #9d9; border-color: #696; color: #393;
-}
-#content.build #builds ul.steps li.failed {
-  background: #d99 url(failure.png) 2px .3em no-repeat; border-color: #966;
-  color: #933;
-}
-#content.build #builds ul.steps li :link,
-#content.build #builds ul.steps li :visited { border: none; color: inherit;
-  font-weight: bold; text-decoration: none;
-}
-#content.build #builds ul.steps li .duration { float: right;
-  font-size: smaller;
-}
-#content.build #builds ul.steps li.success .duration { color: #696; }
-#content.build #builds ul.steps li.failed .duration { color: #966; }
-#content.build #builds ul.steps li.failed ul { font-size: smaller;
-  line-height: 1.2em; list-style-type: square; margin: 0;
-  padding: 0 0 .5em 1.5em;
-}
-
-#content.build #overview { line-height: 130%; margin-top: 1em; padding: .5em; }
-#content.build #overview dt { font-weight: bold; padding-right: .25em;
- position: absolute; left: 0; text-align: right; width: 11.5em;
-}
-#content.build #overview dd { margin-left: 12em; }
-#content.build #overview .slave { margin-top: 1em; }
-#content.build #overview .time { margin-top: 1em; }
-
-#content.build div.errors { background: #d99; border: 1px solid #966;
-  color: #933; float: right; margin: 1em;
-}
-#content.build div.errors h3 { background: #966; color: #fff; margin: 0;
-  padding: 0 .3em;
-}
-#content.build div.errors ul { list-style-image: url(failure.png); margin: 0;
-  padding: .5em 1.75em;
-}
-
-#content.build .tabs { clear: right; list-style: none; float: left; width: 100%;
-  margin: 0 1em; padding: 0;
-}
-#content.build .tabs li { cursor: pointer; float: left; }
-#content.build .tabs li a { background: #b9b9b9; color: #666; display: block;
-  margin: 2px 2px 0; padding: 3px 2em 0;
-}
-#content.build .tabs li a:hover { color: #333; text-decoration: none; }
-#content.build .tabs li.active a { background: #d7d7d7; border: 1px outset;
-  border-bottom: none; color: #333; font-weight: bold; margin-top: 0;
-  padding-bottom: 1px;
-}
-#content.build .tab-content { background: #f4f4f4; border: 1px outset;
-  clear: both; margin: 0 2em 0 1em; padding: 5px;
-}
-#content.build .tab-content table { margin: 0; }
-
-#content.build tbody.totals td, #content.build tbody.totals th {
-  font-weight: bold;
-}
-#content.build table.tests tr.failed { background: #d99; }
-#content.build table.tests tr.failed td { font-weight: bold; }
-#content.build table.tests tr.failed:hover th,
-#content.build table.tests tr.failed:hover td,
-#content.build table.tests tr.failed:hover tr { background: #966; }
-#content.build table.tests tr.failed :link,
-#content.build table.tests tr.failed :visited { color: #933 }
-#content.build table.tests tr.failed :link:hover,
-#content.build table.tests tr.failed :visited:hover { color: #fff; }
-
-#content.build .log { background: #fff; border: 1px inset; font-size: 90%;
-  overflow: auto; max-height: 20em; width: 100%; white-space: pre;
-}
-#content.build .log code { padding: 0 5px; }
-#content.build .log .warning { color: #660; font-weight: bold; }
-#content.build .log .error { color: #900; font-weight: bold; }
-
-#content.build table.listing th, #content.build table.listing td {
-  font-size: 95%;
-}
-#content.build table.listing tbody th, #content.build table.listing tbody td {
-  background: #fff; padding: .1em .3em;
-}
-#content.build table.listing :link, #content.build table.listing :visited {
-  border: none;
-}
deleted file mode 100755
index 1e2f5669121b121d8119ca1ca05ff66663a41075..e69de29bb2d1d6434b8b29ae775ad8c2e48c5391
GIT binary patch
literal 0
Hc$@<O00001
deleted file mode 100644
index a5b902f0caf9aa53a97b38b3cc0436f483f24d92..e69de29bb2d1d6434b8b29ae775ad8c2e48c5391
GIT binary patch
literal 0
Hc$@<O00001
deleted file mode 100644
--- a/trac-0.11/bitten/htdocs/bitten_coverage.css
+++ /dev/null
@@ -1,4 +0,0 @@
-/* Code coverage file annotations */
-table.code th.coverage { width: 4em; }
-table.code th.covered { background-color: #0f0; }
-table.code th.uncovered { background-color: #f00; }
deleted file mode 100644
index e2821facdb1d55065baf6a435dafe72de5d8e777..e69de29bb2d1d6434b8b29ae775ad8c2e48c5391
GIT binary patch
literal 0
Hc$@<O00001
deleted file mode 100644
index cbd15765f453d2ac3725c7740c87be1593c3f5db..e69de29bb2d1d6434b8b29ae775ad8c2e48c5391
GIT binary patch
literal 0
Hc$@<O00001
deleted file mode 100644
index 268f8e3042f54d4fccbc9161c338c9f465412609..e69de29bb2d1d6434b8b29ae775ad8c2e48c5391
GIT binary patch
literal 0
Hc$@<O00001
deleted file mode 100644
index 7b61a52c4cb18bafc176d2fc35def4b617037cbc..e69de29bb2d1d6434b8b29ae775ad8c2e48c5391
GIT binary patch
literal 0
Hc$@<O00001
deleted file mode 100644
index 30d4aa2dea3c059fdc08b7900e756018f8fe1c01..e69de29bb2d1d6434b8b29ae775ad8c2e48c5391
GIT binary patch
literal 0
Hc$@<O00001
deleted file mode 100644
index c67ab32fafc00d2c3a3f90df799452111eec4d39..e69de29bb2d1d6434b8b29ae775ad8c2e48c5391
GIT binary patch
literal 0
Hc$@<O00001
deleted file mode 100644
index 1c2f7ffbc7eef1402abcc7a381ad11e42c292cca..e69de29bb2d1d6434b8b29ae775ad8c2e48c5391
GIT binary patch
literal 0
Hc$@<O00001
deleted file mode 100644
index 3d3da8e80ce449c0d0c9b2853073942e598dc209..e69de29bb2d1d6434b8b29ae775ad8c2e48c5391
GIT binary patch
literal 0
Hc$@<O00001
deleted file mode 100644
index edc00972484cc0591683f7f0d9d55b423d81c92c..e69de29bb2d1d6434b8b29ae775ad8c2e48c5391
GIT binary patch
literal 0
Hc$@<O00001
deleted file mode 100644
index a5e6b7973a525c8738b733036c3dda2330ea4b02..e69de29bb2d1d6434b8b29ae775ad8c2e48c5391
GIT binary patch
literal 0
Hc$@<O00001
deleted file mode 100644
index 866116d4de72f211e1ff7a0bece0bd0f70222934..e69de29bb2d1d6434b8b29ae775ad8c2e48c5391
GIT binary patch
literal 0
Hc$@<O00001
deleted file mode 100644
index 811e3d26d9e3056b2145b0bed6eabb16b5651ada..e69de29bb2d1d6434b8b29ae775ad8c2e48c5391
GIT binary patch
literal 0
Hc$@<O00001
deleted file mode 100644
index a740afab2e9dcb9b94738b57cc63388e35f1cb60..e69de29bb2d1d6434b8b29ae775ad8c2e48c5391
GIT binary patch
literal 0
Hc$@<O00001
deleted file mode 100644
index 4d7dd27b1e90089a091d707f7ce747634ce2e7a2..e69de29bb2d1d6434b8b29ae775ad8c2e48c5391
GIT binary patch
literal 0
Hc$@<O00001
deleted file mode 100644
index f682a88233e0675c5561588423ed6cc00e9d2004..e69de29bb2d1d6434b8b29ae775ad8c2e48c5391
GIT binary patch
literal 0
Hc$@<O00001
deleted file mode 100644
index 7d450c40f902be61743defaae782269e2984bf14..e69de29bb2d1d6434b8b29ae775ad8c2e48c5391
GIT binary patch
literal 0
Hc$@<O00001
deleted file mode 100644
index f2f2d537b1fd0f96654f8f1005dc2ad0206f2eb9..e69de29bb2d1d6434b8b29ae775ad8c2e48c5391
GIT binary patch
literal 0
Hc$@<O00001
deleted file mode 100644
index c452702490bd5c175c0716acc03c79bb5c0ede47..e69de29bb2d1d6434b8b29ae775ad8c2e48c5391
GIT binary patch
literal 0
Hc$@<O00001
deleted file mode 100644
index 702258c3d72fb128148646894daed3c9facc0513..e69de29bb2d1d6434b8b29ae775ad8c2e48c5391
GIT binary patch
literal 0
Hc$@<O00001
deleted file mode 100644
--- a/trac-0.11/bitten/htdocs/tabset.js
+++ /dev/null
@@ -1,54 +0,0 @@
-function makeTabSet(parentElement) {
-  var tabList = document.createElement("ul");
-  tabList.className = "tabs";
-  var contentDivs = document.createElement("div");
-
-  function makeTab(div) {
-    var title = div.firstChild;
-    while (title.nodeType != 1) title = title.nextSibling;
-    var tabItem = document.createElement("li");
-    if (!tabList.childNodes.length) tabItem.className = "active";
-    var link = document.createElement("a");
-    link.href = "#";
-    link.appendChild(title.firstChild);
-    tabItem.appendChild(link);
-
-    var contentDiv = document.createElement("div");
-    contentDiv.className = "tab-content";
-    while (div.childNodes.length) contentDiv.appendChild(div.firstChild);
-    if (tabList.childNodes.length) contentDiv.style.display = "none";
-
-    link.onclick = function() {
-      var child = contentDivs.firstChild;
-      while (child) {
-        if (child != contentDiv && child.nodeType == 1) {
-          child.style.display = "none";
-        }
-        child = child.nextSibling;
-      }
-      var item = tabList.firstChild;
-      while (item) {
-        if (item.nodeType == 1) {
-          item.className = item != tabItem ? "" : "active";
-        }
-        item = item.nextSibling;
-      }
-      contentDiv.style.display = "block";
-      return false;
-    }
-    contentDivs.appendChild(contentDiv);
-    tabList.appendChild(tabItem);
-  }
-
-  var divs = parentElement.getElementsByTagName("div");
-  for (var i = 0; i < divs.length; i++) {
-    var div = divs[i];
-    if (!/\btab\b/.test(div.className)) {
-      continue;
-    }
-    makeTab(div);
-  }
-
-  parentElement.appendChild(tabList);
-  parentElement.appendChild(contentDivs);
-}
deleted file mode 100644
--- a/trac-0.11/bitten/main.py
+++ /dev/null
@@ -1,104 +0,0 @@
-# -*- 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 inspect
-import os
-import textwrap
-
-from trac.core import *
-from trac.db import DatabaseManager
-from trac.env import IEnvironmentSetupParticipant
-from trac.perm import IPermissionRequestor
-from trac.wiki import IWikiSyntaxProvider
-from bitten.api import IBuildListener
-from bitten.model import schema, schema_version, Build, BuildConfig
-
-__all__ = ['BuildSystem']
-__docformat__ = 'restructuredtext en'
-
-
-class BuildSystem(Component):
-
-    implements(IEnvironmentSetupParticipant, IPermissionRequestor,
-               IWikiSyntaxProvider)
-
-    listeners = ExtensionPoint(IBuildListener)
-
-    # IEnvironmentSetupParticipant methods
-
-    def environment_created(self):
-        # Create the required tables
-        db = self.env.get_db_cnx()
-        connector, _ = DatabaseManager(self.env)._get_connector()
-        cursor = db.cursor()
-        for table in schema:
-            for stmt in connector.to_sql(table):
-                cursor.execute(stmt)
-
-        # Insert a global version flag
-        cursor.execute("INSERT INTO system (name,value) "
-                       "VALUES ('bitten_version',%s)", (schema_version,))
-
-        # Create the directory for storing snapshot archives
-        snapshots_dir = os.path.join(self.env.path, 'snapshots')
-        os.mkdir(snapshots_dir)
-
-        db.commit()
-
-    def environment_needs_upgrade(self, db):
-        cursor = db.cursor()
-        cursor.execute("SELECT value FROM system WHERE name='bitten_version'")
-        row = cursor.fetchone()
-        if not row or int(row[0]) < schema_version:
-            return True
-
-    def upgrade_environment(self, db):
-        cursor = db.cursor()
-        cursor.execute("SELECT value FROM system WHERE name='bitten_version'")
-        row = cursor.fetchone()
-        if not row:
-            self.environment_created()
-        else:
-            current_version = int(row[0])
-            from bitten import upgrades
-            for version in range(current_version + 1, schema_version + 1):
-                for function in upgrades.map.get(version):
-                    print textwrap.fill(inspect.getdoc(function))
-                    function(self.env, db)
-                    print 'Done.'
-            cursor.execute("UPDATE system SET value=%s WHERE "
-                           "name='bitten_version'", (schema_version,))
-            self.log.info('Upgraded Bitten tables from version %d to %d',
-                          current_version, schema_version)
-
-    # IPermissionRequestor methods
-
-    def get_permission_actions(self):
-        actions = ['BUILD_VIEW', 'BUILD_CREATE', 'BUILD_MODIFY', 'BUILD_DELETE',
-                   'BUILD_EXEC']
-        return actions + [('BUILD_ADMIN', actions)]
-
-    # IWikiSyntaxProvider methods
-
-    def get_wiki_syntax(self):
-        return []
-
-    def get_link_resolvers(self):
-        def _format_link(formatter, ns, name, label):
-            build = Build.fetch(self.env, int(name))
-            if build:
-                config = BuildConfig.fetch(self.env, build.config)
-                title = 'Build %d ([%s] of %s) by %s' % (build.id, build.rev,
-                        config.label, build.slave)
-                return '<a class="build" href="%s" title="%s">%s</a>' \
-                       % (formatter.href.build(build.config, build.id), title,
-                          label)
-            return label
-        yield 'build', _format_link
deleted file mode 100644
--- a/trac-0.11/bitten/master.py
+++ /dev/null
@@ -1,314 +0,0 @@
-# -*- coding: utf-8 -*-
-#
-# Copyright (C) 2007 Edgewall Software
-# Copyright (C) 2005-2007 Christopher Lenz <cmlenz@gmx.de>
-# 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.
-
-"""Build master implementation."""
-
-import calendar
-import re
-import time
-
-from trac.config import BoolOption, IntOption
-from trac.core import *
-from trac.web import IRequestHandler, HTTPBadRequest, HTTPConflict, \
-                     HTTPForbidden, HTTPMethodNotAllowed, HTTPNotFound, \
-                     RequestDone
-
-from bitten.model import BuildConfig, Build, BuildStep, BuildLog, Report
-from bitten.main import BuildSystem
-from bitten.queue import BuildQueue
-from bitten.recipe import Recipe
-from bitten.util import xmlio
-
-__all__ = ['BuildMaster']
-__docformat__ = 'restructuredtext en'
-
-
-class BuildMaster(Component):
-    """BEEP listener implementation for the build master."""
-
-    implements(IRequestHandler)
-
-    # Configuration options
-
-    adjust_timestamps = BoolOption('bitten', 'adjust_timestamps', False, doc=
-        """Whether the timestamps of builds should be adjusted to be close
-        to the timestamps of the corresponding changesets.""")
-
-    build_all = BoolOption('bitten', 'build_all', False, doc=
-        """Whether to request builds of older revisions even if a younger
-        revision has already been built.""")
-    
-    stabilize_wait = IntOption('bitten', 'stabilize_wait', 0, doc=
-        """The time in seconds to wait for the repository to stabilize before
-        queuing up a new build.  This allows time for developers to check in
-        a group of related changes back to back without spawning multiple
-        builds.""")
-
-    slave_timeout = IntOption('bitten', 'slave_timeout', 3600, doc=
-        """The time in seconds after which a build is cancelled if the slave
-        does not report progress.""")
-
-    # IRequestHandler methods
-
-    def match_request(self, req):
-        match = re.match(r'/builds(?:/(\d+)(?:/(\w+)/([^/]+)?)?)?$',
-                         req.path_info)
-        if match:
-            if match.group(1):
-                req.args['id'] = match.group(1)
-                req.args['collection'] = match.group(2)
-                req.args['member'] = match.group(3)
-            return True
-
-    def process_request(self, req):
-        req.perm.assert_permission('BUILD_EXEC')
-
-        if 'id' not in req.args:
-            if req.method != 'POST':
-                raise HTTPMethodNotAllowed('Method not allowed')
-            return self._process_build_creation(req)
-
-        build = Build.fetch(self.env, req.args['id'])
-        if not build:
-            raise HTTPNotFound('No such build')
-        config = BuildConfig.fetch(self.env, build.config)
-
-        if not req.args['collection']:
-            if req.method == 'DELETE':
-                return self._process_build_cancellation(req, config, build)
-            else:
-                return self._process_build_initiation(req, config, build)
-
-        if req.method != 'POST':
-            raise HTTPMethodNotAllowed('Method not allowed')
-
-        if req.args['collection'] == 'steps':
-            return self._process_build_step(req, config, build)
-        else:
-            raise HTTPNotFound('No such collection')
-
-    def _process_build_creation(self, req):
-        queue = BuildQueue(self.env, build_all=self.build_all, 
-                           stabilize_wait=self.stabilize_wait,
-                           timeout=self.slave_timeout)
-        queue.populate()
-
-        try:
-            elem = xmlio.parse(req.read())
-        except xmlio.ParseError, e:
-            self.log.error('Error parsing build initialization request: %s', e,
-                           exc_info=True)
-            raise HTTPBadRequest('XML parser error')
-
-        slavename = elem.attr['name']
-        properties = {'name': slavename, Build.IP_ADDRESS: req.remote_addr}
-        self.log.info('Build slave %r connected from %s', slavename,
-                      req.remote_addr)
-
-        for child in elem.children():
-            if child.name == 'platform':
-                properties[Build.MACHINE] = child.gettext()
-                properties[Build.PROCESSOR] = child.attr.get('processor')
-            elif child.name == 'os':
-                properties[Build.OS_NAME] = child.gettext()
-                properties[Build.OS_FAMILY] = child.attr.get('family')
-                properties[Build.OS_VERSION] = child.attr.get('version')
-            elif child.name == 'package':
-                for name, value in child.attr.items():
-                    if name == 'name':
-                        continue
-                    properties[child.attr['name'] + '.' + name] = value
-
-        self.log.debug('Build slave configuration: %r', properties)
-
-        build = queue.get_build_for_slave(slavename, properties)
-        if not build:
-            req.send_response(204)
-            req.write('')
-            raise RequestDone
-
-        req.send_response(201)
-        req.send_header('Content-Type', 'text/plain')
-        req.send_header('Location', req.abs_href.builds(build.id))
-        req.write('Build pending')
-        raise RequestDone
-
-    def _process_build_cancellation(self, req, config, build):
-        self.log.info('Build slave %r cancelled build %d', build.slave,
-                      build.id)
-        build.status = Build.PENDING
-        build.slave = None
-        build.slave_info = {}
-        build.started = 0
-        db = self.env.get_db_cnx()
-        for step in list(BuildStep.select(self.env, build=build.id, db=db)):
-            step.delete(db=db)
-        build.update(db=db)
-        db.commit()
-
-        for listener in BuildSystem(self.env).listeners:
-            listener.build_aborted(build)
-
-        req.send_response(204)
-        req.write('')
-        raise RequestDone
-
-    def _process_build_initiation(self, req, config, build):
-        self.log.info('Build slave %r initiated build %d', build.slave,
-                      build.id)
-        build.started = int(time.time())
-        build.update()
-
-        for listener in BuildSystem(self.env).listeners:
-            listener.build_started(build)
-
-        xml = xmlio.parse(config.recipe)
-        xml.attr['path'] = config.path
-        xml.attr['revision'] = build.rev
-        xml.attr['config'] = config.name
-        xml.attr['build'] = str(build.id)
-        body = str(xml)
-
-        self.log.info('Build slave %r initiated build %d', build.slave,
-                      build.id)
-
-        req.send_response(200)
-        req.send_header('Content-Type', 'application/x-bitten+xml')
-        req.send_header('Content-Length', str(len(body)))
-        req.send_header('Content-Disposition',
-                        'attachment; filename=recipe_%s_r%s.xml' %
-                        (config.name, build.rev))
-        req.write(body)
-        raise RequestDone
-
-    def _process_build_step(self, req, config, build):
-        try:
-            elem = xmlio.parse(req.read())
-        except xmlio.ParseError, e:
-            self.log.error('Error parsing build step result: %s', e,
-                           exc_info=True)
-            raise HTTPBadRequest('XML parser error')
-        stepname = elem.attr['step']
-	
-        # make sure it's the right slave.
-        if build.status != Build.IN_PROGRESS or \
-                build.slave_info.get(Build.IP_ADDRESS) != req.remote_addr:
-            raise HTTPForbidden('Build %s has been invalidated for host %s.'
-                                % (build.id, req.remote_addr))
-
-        step = BuildStep.fetch(self.env, build=build.id, name=stepname)
-        if step:
-            raise HTTPConflict('Build step already exists')
-
-        recipe = Recipe(xmlio.parse(config.recipe))
-        index = None
-        current_step = None
-        for num, recipe_step in enumerate(recipe):
-            if recipe_step.id == stepname:
-                index = num
-                current_step = recipe_step
-        if index is None:
-            raise HTTPForbidden('No such build step')
-        last_step = index == num
-
-        self.log.debug('Slave %s (build %d) completed step %d (%s) with '
-                       'status %s', build.slave, build.id, index, stepname,
-                       elem.attr['status'])
-
-        db = self.env.get_db_cnx()
-
-        step = BuildStep(self.env, build=build.id, name=stepname)
-        try:
-            step.started = int(_parse_iso_datetime(elem.attr['time']))
-            step.stopped = step.started + float(elem.attr['duration'])
-        except ValueError, e:
-            self.log.error('Error parsing build step timestamp: %s', e,
-                           exc_info=True)
-            raise HTTPBadRequest(e.args[0])
-        if elem.attr['status'] == 'failure':
-            self.log.warning('Build %s step %s failed', build.id, stepname)
-            step.status = BuildStep.FAILURE
-            if current_step.onerror == 'fail':
-                last_step = True
-        else:
-            step.status = BuildStep.SUCCESS
-        step.errors += [error.gettext() for error in elem.children('error')]
-        step.insert(db=db)
-
-        # Collect log messages from the request body
-        for idx, log_elem in enumerate(elem.children('log')):
-            build_log = BuildLog(self.env, build=build.id, step=stepname,
-                                 generator=log_elem.attr.get('generator'),
-                                 orderno=idx)
-            for message_elem in log_elem.children('message'):
-                build_log.messages.append((message_elem.attr['level'],
-                                           message_elem.gettext()))
-            build_log.insert(db=db)
-
-        # Collect report data from the request body
-        for report_elem in elem.children('report'):
-            report = Report(self.env, build=build.id, step=stepname,
-                            category=report_elem.attr.get('category'),
-                            generator=report_elem.attr.get('generator'))
-            for item_elem in report_elem.children():
-                item = {'type': item_elem.name}
-                item.update(item_elem.attr)
-                for child_elem in item_elem.children():
-                    item[child_elem.name] = child_elem.gettext()
-                report.items.append(item)
-            report.insert(db=db)
-
-        # If this was the last step in the recipe we mark the build as
-        # completed
-        if last_step:
-            self.log.info('Slave %s completed build %d ("%s" as of [%s])',
-                          build.slave, build.id, build.config, build.rev)
-            build.stopped = step.stopped
-
-            # Determine overall outcome of the build by checking the outcome
-            # of the individual steps against the "onerror" specification of
-            # each step in the recipe
-            for num, recipe_step in enumerate(recipe):
-                step = BuildStep.fetch(self.env, build.id, recipe_step.id)
-                if step.status == BuildStep.FAILURE:
-                    if recipe_step.onerror != 'ignore':
-                        build.status = Build.FAILURE
-                        break
-            else:
-                build.status = Build.SUCCESS
-
-            build.update(db=db)
-
-        db.commit()
-
-        if last_step:
-            for listener in BuildSystem(self.env).listeners:
-                listener.build_completed(build)
-
-        body = 'Build step processed'
-        req.send_response(201)
-        req.send_header('Content-Type', 'text/plain')
-        req.send_header('Content-Length', str(len(body)))
-        req.send_header('Location', req.abs_href.builds(build.id, 'steps',
-                        stepname))
-        req.write(body)
-        raise RequestDone
-
-
-def _parse_iso_datetime(string):
-    """Minimal parser for ISO date-time strings.
-    
-    Return the time as floating point number. Only handles UTC timestamps
-    without time zone information."""
-    try:
-        string = string.split('.', 1)[0] # strip out microseconds
-        return calendar.timegm(time.strptime(string, '%Y-%m-%dT%H:%M:%S'))
-    except ValueError, e:
-        raise ValueError('Invalid ISO date/time %r' % string)
deleted file mode 100644
--- a/trac-0.11/bitten/model.py
+++ /dev/null
@@ -1,940 +0,0 @@
-# -*- 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.
-
-"""Model classes for objects persisted in the database."""
-
-from trac.db import Table, Column, Index
-
-__docformat__ = 'restructuredtext en'
-
-
-class BuildConfig(object):
-    """Representation of a build configuration."""
-
-    _schema = [
-        Table('bitten_config', key='name')[
-            Column('name'), Column('path'), Column('active', type='int'),
-            Column('recipe'), Column('min_rev'), Column('max_rev'),
-            Column('label'), Column('description')
-        ]
-    ]
-
-    def __init__(self, env, name=None, path=None, active=False, recipe=None,
-                 min_rev=None, max_rev=None, label=None, description=None):
-        """Initialize a new build configuration with the specified attributes.
-
-        To actually create this configuration in the database, the `insert`
-        method needs to be called.
-        """
-        self.env = env
-        self._old_name = None
-        self.name = name
-        self.path = path or ''
-        self.active = bool(active)
-        self.recipe = recipe or ''
-        self.min_rev = min_rev or None
-        self.max_rev = max_rev or None
-        self.label = label or ''
-        self.description = description or ''
-
-    def __repr__(self):
-        return '<%s %r>' % (type(self).__name__, self.name)
-
-    exists = property(fget=lambda self: self._old_name is not None,
-                      doc='Whether this configuration exists in the database')
-
-    def delete(self, db=None):
-        """Remove a build configuration and all dependent objects from the
-        database."""
-        assert self.exists, 'Cannot delete non-existing configuration'
-        if not db:
-            db = self.env.get_db_cnx()
-            handle_ta = True
-        else:
-            handle_ta = False
-
-        for platform in list(TargetPlatform.select(self.env, self.name, db=db)):
-            platform.delete(db=db)
-
-        for build in list(Build.select(self.env, config=self.name, db=db)):
-            build.delete(db=db)
-
-        cursor = db.cursor()
-        cursor.execute("DELETE FROM bitten_config WHERE name=%s", (self.name,))
-
-        if handle_ta:
-            db.commit()
-        self._old_name = None
-
-    def insert(self, db=None):
-        """Insert a new configuration into the database."""
-        assert not self.exists, 'Cannot insert existing configuration'
-        assert self.name, 'Configuration requires a name'
-        if not db:
-            db = self.env.get_db_cnx()
-            handle_ta = True
-        else:
-            handle_ta = False
-
-        cursor = db.cursor()
-        cursor.execute("INSERT INTO bitten_config (name,path,active,"
-                       "recipe,min_rev,max_rev,label,description) "
-                       "VALUES (%s,%s,%s,%s,%s,%s,%s,%s)",
-                       (self.name, self.path, int(self.active or 0),
-                        self.recipe or '', self.min_rev, self.max_rev,
-                        self.label or '', self.description or ''))
-
-        if handle_ta:
-            db.commit()
-        self._old_name = self.name
-
-    def update(self, db=None):
-        """Save changes to an existing build configuration."""
-        assert self.exists, 'Cannot update a non-existing configuration'
-        assert self.name, 'Configuration requires a name'
-        if not db:
-            db = self.env.get_db_cnx()
-            handle_ta = True
-        else:
-            handle_ta = False
-
-        cursor = db.cursor()
-        cursor.execute("UPDATE bitten_config SET name=%s,path=%s,active=%s,"
-                       "recipe=%s,min_rev=%s,max_rev=%s,label=%s,"
-                       "description=%s WHERE name=%s",
-                       (self.name, self.path, int(self.active or 0),
-                        self.recipe, self.min_rev, self.max_rev,
-                        self.label, self.description, self._old_name))
-        if self.name != self._old_name:
-            cursor.execute("UPDATE bitten_platform SET config=%s "
-                           "WHERE config=%s", (self.name, self._old_name))
-            cursor.execute("UPDATE bitten_build SET config=%s "
-                           "WHERE config=%s", (self.name, self._old_name))
-
-        if handle_ta:
-            db.commit()
-        self._old_name = self.name
-
-    def fetch(cls, env, name, db=None):
-        """Retrieve an existing build configuration from the database by
-        name.
-        """
-        if not db:
-            db = env.get_db_cnx()
-
-        cursor = db.cursor()
-        cursor.execute("SELECT path,active,recipe,min_rev,max_rev,label,"
-                       "description FROM bitten_config WHERE name=%s", (name,))
-        row = cursor.fetchone()
-        if not row:
-            return None
-
-        config = BuildConfig(env)
-        config.name = config._old_name = name
-        config.path = row[0] or ''
-        config.active = bool(row[1])
-        config.recipe = row[2] or ''
-        config.min_rev = row[3] or None
-        config.max_rev = row[4] or None
-        config.label = row[5] or ''
-        config.description = row[6] or ''
-        return config
-
-    fetch = classmethod(fetch)
-
-    def select(cls, env, include_inactive=False, db=None):
-        """Retrieve existing build configurations from the database that match
-        the specified criteria.
-        """
-        if not db:
-            db = env.get_db_cnx()
-
-        cursor = db.cursor()
-        if include_inactive:
-            cursor.execute("SELECT name,path,active,recipe,min_rev,max_rev,"
-                           "label,description FROM bitten_config ORDER BY name")
-        else:
-            cursor.execute("SELECT name,path,active,recipe,min_rev,max_rev,"
-                           "label,description FROM bitten_config "
-                           "WHERE active=1 ORDER BY name")
-        for name, path, active, recipe, min_rev, max_rev, label, description \
-                in cursor:
-            config = BuildConfig(env, name=name, path=path or '',
-                                 active=bool(active), recipe=recipe or '',
-                                 min_rev=min_rev or None,
-                                 max_rev=max_rev or None, label=label or '',
-                                 description=description or '')
-            config._old_name = name
-            yield config
-
-    select = classmethod(select)
-
-
-class TargetPlatform(object):
-    """Target platform for a build configuration."""
-
-    _schema = [
-        Table('bitten_platform', key='id')[
-            Column('id', auto_increment=True), Column('config'), Column('name')
-        ],
-        Table('bitten_rule', key=('id', 'propname'))[
-            Column('id'), Column('propname'), Column('pattern'),
-            Column('orderno', type='int')
-        ]
-    ]
-
-    def __init__(self, env, config=None, name=None):
-        """Initialize a new target platform with the specified attributes.
-
-        To actually create this platform in the database, the `insert` method
-        needs to be called.
-        """
-        self.env = env
-        self.id = None
-        self.config = config
-        self.name = name
-        self.rules = []
-
-    def __repr__(self):
-        return '<%s %r>' % (type(self).__name__, self.id)
-
-    exists = property(fget=lambda self: self.id is not None,
-                      doc='Whether this target platform exists in the database')
-
-    def delete(self, db=None):
-        """Remove the target platform from the database."""
-        if not db:
-            db = self.env.get_db_cnx()
-            handle_ta = True
-        else:
-            handle_ta = False
-
-        cursor = db.cursor()
-        cursor.execute("DELETE FROM bitten_rule WHERE id=%s", (self.id,))
-        cursor.execute("DELETE FROM bitten_platform WHERE id=%s", (self.id,))
-        if handle_ta:
-            db.commit()
-
-    def insert(self, db=None):
-        """Insert a new target platform into the database."""
-        if not db:
-            db = self.env.get_db_cnx()
-            handle_ta = True
-        else:
-            handle_ta = False
-
-        assert not self.exists, 'Cannot insert existing target platform'
-        assert self.config, 'Target platform needs to be associated with a ' \
-                            'configuration'
-        assert self.name, 'Target platform requires a name'
-
-        cursor = db.cursor()
-        cursor.execute("INSERT INTO bitten_platform (config,name) "
-                       "VALUES (%s,%s)", (self.config, self.name))
-        self.id = db.get_last_id(cursor, 'bitten_platform')
-        if self.rules:
-            cursor.executemany("INSERT INTO bitten_rule VALUES (%s,%s,%s,%s)",
-                               [(self.id, propname, pattern, idx)
-                                for idx, (propname, pattern)
-                                in enumerate(self.rules)])
-
-        if handle_ta:
-            db.commit()
-
-    def update(self, db=None):
-        """Save changes to an existing target platform."""
-        assert self.exists, 'Cannot update a non-existing platform'
-        assert self.config, 'Target platform needs to be associated with a ' \
-                            'configuration'
-        assert self.name, 'Target platform requires a name'
-        if not db:
-            db = self.env.get_db_cnx()
-            handle_ta = True
-        else:
-            handle_ta = False
-
-        cursor = db.cursor()
-        cursor.execute("UPDATE bitten_platform SET name=%s WHERE id=%s",
-                       (self.name, self.id))
-        cursor.execute("DELETE FROM bitten_rule WHERE id=%s", (self.id,))
-        if self.rules:
-            cursor.executemany("INSERT INTO bitten_rule VALUES (%s,%s,%s,%s)",
-                               [(self.id, propname, pattern, idx)
-                                for idx, (propname, pattern)
-                                in enumerate(self.rules)])
-
-        if handle_ta:
-            db.commit()
-
-    def fetch(cls, env, id, db=None):
-        """Retrieve an existing target platform from the database by ID."""
-        if not db:
-            db = env.get_db_cnx()
-
-        cursor = db.cursor()
-        cursor.execute("SELECT config,name FROM bitten_platform "
-                       "WHERE id=%s", (id,))
-        row = cursor.fetchone()
-        if not row:
-            return None
-
-        platform = TargetPlatform(env, config=row[0], name=row[1])
-        platform.id = id
-        cursor.execute("SELECT propname,pattern FROM bitten_rule "
-                       "WHERE id=%s ORDER BY orderno", (id,))
-        for propname, pattern in cursor:
-            platform.rules.append((propname, pattern))
-        return platform
-
-    fetch = classmethod(fetch)
-
-    def select(cls, env, config=None, db=None):
-        """Retrieve existing target platforms from the database that match the
-        specified criteria.
-        """
-        if not db:
-            db = env.get_db_cnx()
-
-        where_clauses = []
-        if config is not None:
-            where_clauses.append(("config=%s", config))
-        if where_clauses:
-            where = "WHERE " + " AND ".join([wc[0] for wc in where_clauses])
-        else:
-            where = ""
-
-        cursor = db.cursor()
-        cursor.execute("SELECT id FROM bitten_platform %s ORDER BY name"
-                       % where, [wc[1] for wc in where_clauses])
-        for (id,) in cursor:
-            yield TargetPlatform.fetch(env, id)
-
-    select = classmethod(select)
-
-
-class Build(object):
-    """Representation of a build."""
-
-    _schema = [
-        Table('bitten_build', key='id')[
-            Column('id', auto_increment=True), Column('config'), Column('rev'),
-            Column('rev_time', type='int'), Column('platform', type='int'),
-            Column('slave'), Column('started', type='int'),
-            Column('stopped', type='int'), Column('status', size=1),
-            Index(['config', 'rev', 'slave'])
-        ],
-        Table('bitten_slave', key=('build', 'propname'))[
-            Column('build', type='int'), Column('propname'), Column('propvalue')
-        ]
-    ]
-
-    # Build status codes
-    PENDING = 'P'
-    IN_PROGRESS = 'I'
-    SUCCESS = 'S'
-    FAILURE = 'F'
-
-    # Standard slave properties
-    IP_ADDRESS = 'ipnr'
-    MAINTAINER = 'owner'
-    OS_NAME = 'os'
-    OS_FAMILY = 'family'
-    OS_VERSION = 'version'
-    MACHINE = 'machine'
-    PROCESSOR = 'processor'
-
-    def __init__(self, env, config=None, rev=None, platform=None, slave=None,
-                 started=0, stopped=0, rev_time=0, status=PENDING):
-        """Initialize a new build with the specified attributes.
-
-        To actually create this build in the database, the `insert` method needs
-        to be called.
-        """
-        self.env = env
-        self.id = None
-        self.config = config
-        self.rev = rev and str(rev) or None
-        self.platform = platform
-        self.slave = slave
-        self.started = started or 0
-        self.stopped = stopped or 0
-        self.rev_time = rev_time
-        self.status = status
-        self.slave_info = {}
-
-    def __repr__(self):
-        return '<%s %r>' % (type(self).__name__, self.id)
-
-    exists = property(fget=lambda self: self.id is not None,
-                      doc='Whether this build exists in the database')
-    completed = property(fget=lambda self: self.status != Build.IN_PROGRESS,
-                         doc='Whether the build has been completed')
-    successful = property(fget=lambda self: self.status == Build.SUCCESS,
-                          doc='Whether the build was successful')
-
-    def delete(self, db=None):
-        """Remove the build from the database."""
-        assert self.exists, 'Cannot delete a non-existing build'
-        if not db:
-            db = self.env.get_db_cnx()
-            handle_ta = True
-        else:
-            handle_ta = False
-
-        for step in list(BuildStep.select(self.env, build=self.id)):
-            step.delete(db=db)
-
-        cursor = db.cursor()
-        cursor.execute("DELETE FROM bitten_slave WHERE build=%s", (self.id,))
-        cursor.execute("DELETE FROM bitten_build WHERE id=%s", (self.id,))
-
-        if handle_ta:
-            db.commit()
-
-    def insert(self, db=None):
-        """Insert a new build into the database."""
-        assert not self.exists, 'Cannot insert an existing build'
-        if not db:
-            db = self.env.get_db_cnx()
-            handle_ta = True
-        else:
-            handle_ta = False
-
-        assert self.config and self.rev and self.rev_time and self.platform
-        assert self.status in (self.PENDING, self.IN_PROGRESS, self.SUCCESS,
-                               self.FAILURE)
-        if not self.slave:
-            assert self.status == self.PENDING
-
-        cursor = db.cursor()
-        cursor.execute("INSERT INTO bitten_build (config,rev,rev_time,platform,"
-                       "slave,started,stopped,status) "
-                       "VALUES (%s,%s,%s,%s,%s,%s,%s,%s)",
-                       (self.config, self.rev, int(self.rev_time),
-                        self.platform, self.slave or '', self.started or 0,
-                        self.stopped or 0, self.status))
-        self.id = db.get_last_id(cursor, 'bitten_build')
-        if self.slave_info:
-            cursor.executemany("INSERT INTO bitten_slave VALUES (%s,%s,%s)",
-                               [(self.id, name, value) for name, value
-                                in self.slave_info.items()])
-
-        if handle_ta:
-            db.commit()
-
-    def update(self, db=None):
-        """Save changes to an existing build."""
-        assert self.exists, 'Cannot update a non-existing build'
-        if not db:
-            db = self.env.get_db_cnx()
-            handle_ta = True
-        else:
-            handle_ta = False
-
-        assert self.config and self.rev
-        assert self.status in (self.PENDING, self.IN_PROGRESS, self.SUCCESS,
-                               self.FAILURE)
-        if not self.slave:
-            assert self.status == self.PENDING
-
-        cursor = db.cursor()
-        cursor.execute("UPDATE bitten_build SET slave=%s,started=%s,"
-                       "stopped=%s,status=%s WHERE id=%s",
-                       (self.slave or '', self.started or 0,
-                        self.stopped or 0, self.status, self.id))
-        cursor.execute("DELETE FROM bitten_slave WHERE build=%s", (self.id,))
-        if self.slave_info:
-            cursor.executemany("INSERT INTO bitten_slave VALUES (%s,%s,%s)",
-                               [(self.id, name, value) for name, value
-                                in self.slave_info.items()])
-        if handle_ta:
-            db.commit()
-
-    def fetch(cls, env, id, db=None):
-        """Retrieve an existing build from the database by ID."""
-        if not db:
-            db = env.get_db_cnx()
-
-        cursor = db.cursor()
-        cursor.execute("SELECT config,rev,rev_time,platform,slave,started,"
-                       "stopped,status FROM bitten_build WHERE id=%s", (id,))
-        row = cursor.fetchone()
-        if not row:
-            return None
-
-        build = Build(env, config=row[0], rev=row[1], rev_time=int(row[2]),
-                      platform=int(row[3]), slave=row[4],
-                      started=row[5] and int(row[5]) or 0,
-                      stopped=row[6] and int(row[6]) or 0, status=row[7])
-        build.id = int(id)
-        cursor.execute("SELECT propname,propvalue FROM bitten_slave "
-                       "WHERE build=%s", (id,))
-        for propname, propvalue in cursor:
-            build.slave_info[propname] = propvalue
-        return build
-
-    fetch = classmethod(fetch)
-
-    def select(cls, env, config=None, rev=None, platform=None, slave=None,
-               status=None, db=None):
-        """Retrieve existing builds from the database that match the specified
-        criteria.
-        """
-        if not db:
-            db = env.get_db_cnx()
-
-        where_clauses = []
-        if config is not None:
-            where_clauses.append(("config=%s", config))
-        if rev is not None:
-            where_clauses.append(("rev=%s", rev))
-        if platform is not None:
-            where_clauses.append(("platform=%s", platform))
-        if slave is not None:
-            where_clauses.append(("slave=%s", slave))
-        if status is not None:
-            where_clauses.append(("status=%s", status))
-        if where_clauses:
-            where = "WHERE " + " AND ".join([wc[0] for wc in where_clauses])
-        else:
-            where = ""
-
-        cursor = db.cursor()
-        cursor.execute("SELECT id FROM bitten_build %s "
-                       "ORDER BY config,rev_time DESC,slave"
-                       % where, [wc[1] for wc in where_clauses])
-        for (id,) in cursor:
-            yield Build.fetch(env, id)
-    select = classmethod(select)
-
-
-class BuildStep(object):
-    """Represents an individual step of an executed build."""
-
-    _schema = [
-        Table('bitten_step', key=('build', 'name'))[
-            Column('build', type='int'), Column('name'), Column('description'),
-            Column('status', size=1), Column('started', type='int'),
-            Column('stopped', type='int')
-        ],
-        Table('bitten_error', key=('build', 'step', 'orderno'))[
-            Column('build', type='int'), Column('step'), Column('message'),
-            Column('orderno', type='int')
-        ]
-    ]
-
-    # Step status codes
-    SUCCESS = 'S'
-    FAILURE = 'F'
-
-    def __init__(self, env, build=None, name=None, description=None,
-                 status=None, started=None, stopped=None):
-        """Initialize a new build step with the specified attributes.
-
-        To actually create this build step in the database, the `insert` method
-        needs to be called.
-        """
-        self.env = env
-        self.build = build
-        self.name = name
-        self.description = description
-        self.status = status
-        self.started = started
-        self.stopped = stopped
-        self.errors = []
-        self._exists = False
-
-    exists = property(fget=lambda self: self._exists,
-                      doc='Whether this build step exists in the database')
-    successful = property(fget=lambda self: self.status == BuildStep.SUCCESS,
-                          doc='Whether the build step was successful')
-
-    def delete(self, db=None):
-        """Remove the build step from the database."""
-        if not db:
-            db = self.env.get_db_cnx()
-            handle_ta = True
-        else:
-            handle_ta = False
-
-        for log in list(BuildLog.select(self.env, build=self.build,
-                                        step=self.name, db=db)):
-            log.delete(db=db)
-        for report in list(Report.select(self.env, build=self.build,
-                                         step=self.name, db=db)):
-            report.delete(db=db)
-
-        cursor = db.cursor()
-        cursor.execute("DELETE FROM bitten_step WHERE build=%s AND name=%s",
-                       (self.build, self.name))
-        cursor.execute("DELETE FROM bitten_error WHERE build=%s AND step=%s",
-                       (self.build, self.name))
-
-        if handle_ta:
-            db.commit()
-        self._exists = False
-
-    def insert(self, db=None):
-        """Insert a new build step into the database."""
-        if not db:
-            db = self.env.get_db_cnx()
-            handle_ta = True
-        else:
-            handle_ta = False
-
-        assert self.build and self.name
-        assert self.status in (self.SUCCESS, self.FAILURE)
-
-        cursor = db.cursor()
-        cursor.execute("INSERT INTO bitten_step (build,name,description,status,"
-                       "started,stopped) VALUES (%s,%s,%s,%s,%s,%s)",
-                       (self.build, self.name, self.description or '',
-                        self.status, self.started or 0, self.stopped or 0))
-        if self.errors:
-            cursor.executemany("INSERT INTO bitten_error (build,step,message,"
-                               "orderno) VALUES (%s,%s,%s,%s)",
-                               [(self.build, self.name, message, idx)
-                                for idx, message in enumerate(self.errors)])
-
-        if handle_ta:
-            db.commit()
-        self._exists = True
-
-    def fetch(cls, env, build, name, db=None):
-        """Retrieve an existing build from the database by build ID and step
-        name."""
-        if not db:
-            db = env.get_db_cnx()
-
-        cursor = db.cursor()
-        cursor.execute("SELECT description,status,started,stopped "
-                       "FROM bitten_step WHERE build=%s AND name=%s",
-                       (build, name))
-        row = cursor.fetchone()
-        if not row:
-            return None
-        step = BuildStep(env, build, name, row[0] or '', row[1],
-                         row[2] and int(row[2]), row[3] and int(row[3]))
-        step._exists = True
-
-        cursor.execute("SELECT message FROM bitten_error WHERE build=%s "
-                       "AND step=%s ORDER BY orderno", (build, name))
-        for row in cursor:
-            step.errors.append(row[0] or '')
-        return step
-
-    fetch = classmethod(fetch)
-
-    def select(cls, env, build=None, name=None, status=None, db=None):
-        """Retrieve existing build steps from the database that match the
-        specified criteria.
-        """
-        if not db:
-            db = env.get_db_cnx()
-
-        assert status in (None, BuildStep.SUCCESS, BuildStep.FAILURE)
-
-        where_clauses = []
-        if build is not None:
-            where_clauses.append(("build=%s", build))
-        if name is not None:
-            where_clauses.append(("name=%s", name))
-        if status is not None:
-            where_clauses.append(("status=%s", status))
-        if where_clauses:
-            where = "WHERE " + " AND ".join([wc[0] for wc in where_clauses])
-        else:
-            where = ""
-
-        cursor = db.cursor()
-        cursor.execute("SELECT build,name FROM bitten_step %s ORDER BY stopped"
-                       % where, [wc[1] for wc in where_clauses])
-        for build, name in cursor:
-            yield BuildStep.fetch(env, build, name, db=db)
-
-    select = classmethod(select)
-
-
-class BuildLog(object):
-    """Represents a build log."""
-
-    _schema = [
-        Table('bitten_log', key='id')[
-            Column('id', auto_increment=True), Column('build', type='int'),
-            Column('step'), Column('generator'), Column('orderno', type='int'),
-            Index(['build', 'step'])
-        ],
-        Table('bitten_log_message', key=('log', 'line'))[
-            Column('log', type='int'), Column('line', type='int'),
-            Column('level', size=1), Column('message')
-        ]
-    ]
-
-    # Message levels
-    DEBUG = 'D'
-    INFO = 'I'
-    WARNING = 'W'
-    ERROR = 'E'
-
-    def __init__(self, env, build=None, step=None, generator=None,
-                 orderno=None):
-        """Initialize a new build log with the specified attributes.
-
-        To actually create this build log in the database, the `insert` method
-        needs to be called.
-        """
-        self.env = env
-        self.id = None
-        self.build = build
-        self.step = step
-        self.generator = generator or ''
-        self.orderno = orderno and int(orderno) or 0
-        self.messages = []
-
-    exists = property(fget=lambda self: self.id is not None,
-                      doc='Whether this build log exists in the database')
-
-    def delete(self, db=None):
-        """Remove the build log from the database."""
-        assert self.exists, 'Cannot delete a non-existing build log'
-        if not db:
-            db = self.env.get_db_cnx()
-            handle_ta = True
-        else:
-            handle_ta = False
-
-        cursor = db.cursor()
-        cursor.execute("DELETE FROM bitten_log_message WHERE log=%s",
-                       (self.id,))
-        cursor.execute("DELETE FROM bitten_log WHERE id=%s", (self.id,))
-
-        if handle_ta:
-            db.commit()
-        self.id = None
-
-    def insert(self, db=None):
-        """Insert a new build log into the database."""
-        if not db:
-            db = self.env.get_db_cnx()
-            handle_ta = True
-        else:
-            handle_ta = False
-
-        assert self.build and self.step
-
-        cursor = db.cursor()
-        cursor.execute("INSERT INTO bitten_log (build,step,generator,orderno) "
-                       "VALUES (%s,%s,%s,%s)", (self.build, self.step,
-                       self.generator, self.orderno))
-        id = db.get_last_id(cursor, 'bitten_log')
-        if self.messages:
-            cursor.executemany("INSERT INTO bitten_log_message "
-                               "(log,line,level,message) VALUES (%s,%s,%s,%s)",
-                               [(id, idx, msg[0], msg[1]) for idx, msg in
-                                enumerate(self.messages)])
-
-        if handle_ta:
-            db.commit()
-        self.id = id
-
-    def fetch(cls, env, id, db=None):
-        """Retrieve an existing build from the database by ID."""
-        if not db:
-            db = env.get_db_cnx()
-
-        cursor = db.cursor()
-        cursor.execute("SELECT build,step,generator,orderno FROM bitten_log "
-                       "WHERE id=%s", (id,))
-        row = cursor.fetchone()
-        if not row:
-            return None
-        log = BuildLog(env, int(row[0]), row[1], row[2], row[3])
-        log.id = id
-        cursor.execute("SELECT level,message FROM bitten_log_message "
-                       "WHERE log=%s ORDER BY line", (id,))
-        log.messages = cursor.fetchall() or []
-
-        return log
-
-    fetch = classmethod(fetch)
-
-    def select(cls, env, build=None, step=None, generator=None, db=None):
-        """Retrieve existing build logs from the database that match the
-        specified criteria.
-        """
-        if not db:
-            db = env.get_db_cnx()
-
-        where_clauses = []
-        if build is not None:
-            where_clauses.append(("build=%s", build))
-        if step is not None:
-            where_clauses.append(("step=%s", step))
-        if generator is not None:
-            where_clauses.append(("generator=%s", generator))
-        if where_clauses:
-            where = "WHERE " + " AND ".join([wc[0] for wc in where_clauses])
-        else:
-            where = ""
-
-        cursor = db.cursor()
-        cursor.execute("SELECT id FROM bitten_log %s ORDER BY orderno"
-                       % where, [wc[1] for wc in where_clauses])
-        for (id, ) in cursor:
-            yield BuildLog.fetch(env, id, db=db)
-
-    select = classmethod(select)
-
-
-class Report(object):
-    """Represents a generated report."""
-
-    _schema = [
-        Table('bitten_report', key='id')[
-            Column('id', auto_increment=True), Column('build', type='int'),
-            Column('step'), Column('category'), Column('generator'),
-            Index(['build', 'step', 'category'])
-        ],
-        Table('bitten_report_item', key=('report', 'item', 'name'))[
-            Column('report', type='int'), Column('item', type='int'),
-            Column('name'), Column('value')
-        ]
-    ]
-
-    def __init__(self, env, build=None, step=None, category=None,
-                 generator=None):
-        """Initialize a new report with the specified attributes.
-
-        To actually create this build log in the database, the `insert` method
-        needs to be called.
-        """
-        self.env = env
-        self.id = None
-        self.build = build
-        self.step = step
-        self.category = category
-        self.generator = generator or ''
-        self.items = []
-
-    exists = property(fget=lambda self: self.id is not None,
-                      doc='Whether this report exists in the database')
-
-    def delete(self, db=None):
-        """Remove the report from the database."""
-        assert self.exists, 'Cannot delete a non-existing report'
-        if not db:
-            db = self.env.get_db_cnx()
-            handle_ta = True
-        else:
-            handle_ta = False
-
-        cursor = db.cursor()
-        cursor.execute("DELETE FROM bitten_report_item WHERE report=%s",
-                       (self.id,))
-        cursor.execute("DELETE FROM bitten_report WHERE id=%s", (self.id,))
-
-        if handle_ta:
-            db.commit()
-        self.id = None
-
-    def insert(self, db=None):
-        """Insert a new build log into the database."""
-        if not db:
-            db = self.env.get_db_cnx()
-            handle_ta = True
-        else:
-            handle_ta = False
-
-        assert self.build and self.step and self.category
-
-        # Enforce uniqueness of build-step-category.
-        # This should be done by the database, but the DB schema helpers in Trac
-        # currently don't support UNIQUE() constraints
-        assert not list(Report.select(self.env, build=self.build,
-                                      step=self.step, category=self.category,
-                                      db=db)), 'Report already exists'
-
-        cursor = db.cursor()
-        cursor.execute("INSERT INTO bitten_report "
-                       "(build,step,category,generator) VALUES (%s,%s,%s,%s)",
-                       (self.build, self.step, self.category, self.generator))
-        id = db.get_last_id(cursor, 'bitten_report')
-        for idx, item in enumerate([item for item in self.items if item]):
-            cursor.executemany("INSERT INTO bitten_report_item "
-                               "(report,item,name,value) VALUES (%s,%s,%s,%s)",
-                               [(id, idx, key, value) for key, value
-                                in item.items()])
-
-        if handle_ta:
-            db.commit()
-        self.id = id
-
-    def fetch(cls, env, id, db=None):
-        """Retrieve an existing build from the database by ID."""
-        if not db:
-            db = env.get_db_cnx()
-
-        cursor = db.cursor()
-        cursor.execute("SELECT build,step,category,generator "
-                       "FROM bitten_report WHERE id=%s", (id,))
-        row = cursor.fetchone()
-        if not row:
-            return None
-        report = Report(env, int(row[0]), row[1], row[2] or '', row[3] or '')
-        report.id = id
-
-        cursor.execute("SELECT item,name,value FROM bitten_report_item "
-                       "WHERE report=%s ORDER BY item", (id,))
-        items = {}
-        for item, name, value in cursor:
-            items.setdefault(item, {})[name] = value
-        report.items = items.values()
-
-        return report
-
-    fetch = classmethod(fetch)
-
-    def select(cls, env, config=None, build=None, step=None, category=None,
-               db=None):
-        """Retrieve existing reports from the database that match the specified
-        criteria.
-        """
-        where_clauses = []
-        joins = []
-        if config is not None:
-            where_clauses.append(("config=%s", config))
-            joins.append("INNER JOIN bitten_build ON (bitten_build.id=build)")
-        if build is not None:
-            where_clauses.append(("build=%s", build))
-        if step is not None:
-            where_clauses.append(("step=%s", step))
-        if category is not None:
-            where_clauses.append(("category=%s", category))
-
-        if where_clauses:
-            where = "WHERE " + " AND ".join([wc[0] for wc in where_clauses])
-        else:
-            where = ""
-
-        if not db:
-            db = env.get_db_cnx()
-        cursor = db.cursor()
-        cursor.execute("SELECT bitten_report.id FROM bitten_report %s %s "
-                       "ORDER BY category" % (' '.join(joins), where),
-                       [wc[1] for wc in where_clauses])
-        for (id, ) in cursor:
-            yield Report.fetch(env, id, db=db)
-
-    select = classmethod(select)
-
-
-schema = BuildConfig._schema + TargetPlatform._schema + Build._schema + \
-         BuildStep._schema + BuildLog._schema + Report._schema
-schema_version = 7
deleted file mode 100644
--- a/trac-0.11/bitten/queue.py
+++ /dev/null
@@ -1,314 +0,0 @@
-# -*- coding: utf-8 -*-
-#
-# Copyright (C) 2007 Edgewall Software
-# Copyright (C) 2005-2007 Christopher Lenz <cmlenz@gmx.de>
-# 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.
-
-"""Implements the scheduling of builds for a project.
-
-This module provides the functionality for scheduling builds for a specific
-Trac environment. It is used by both the build master and the web interface to
-get the list of required builds (revisions not built yet).
-
-Furthermore, the `BuildQueue` class is used by the build master to determine
-the next pending build, and to match build slaves against configured target
-platforms.
-"""
-
-from datetime import datetime
-from itertools import ifilter
-import logging
-import re
-import time
-
-from trac.versioncontrol import NoSuchNode
-from bitten.model import BuildConfig, TargetPlatform, Build, BuildStep
-
-__docformat__ = 'restructuredtext en'
-
-log = logging.getLogger('bitten.queue')
-
-
-def collect_changes(repos, config, db=None):
-    """Collect all changes for a build configuration that either have already
-    been built, or still need to be built.
-    
-    This function is a generator that yields ``(platform, rev, build)`` tuples,
-    where ``platform`` is a `TargetPlatform` object, ``rev`` is the identifier
-    of the changeset, and ``build`` is a `Build` object or `None`.
-
-    :param repos: the version control repository
-    :param config: the build configuration
-    :param db: a database connection (optional)
-    """
-    env = config.env
-    if not db:
-        db = env.get_db_cnx()
-    try:
-        node = repos.get_node(config.path)
-    except NoSuchNode, e:
-        env.log.warn('Node for configuration %r not found', config.name,
-                     exc_info=True)
-        return
-
-    for path, rev, chg in node.get_history():
-
-        # Don't follow moves/copies
-        if path != repos.normalize_path(config.path):
-            break
-
-        # Stay within the limits of the build config
-        if config.min_rev and repos.rev_older_than(rev, config.min_rev):
-            break
-        if config.max_rev and repos.rev_older_than(config.max_rev, rev):
-            continue
-
-        # Make sure the repository directory isn't empty at this
-        # revision
-        old_node = repos.get_node(path, rev)
-        is_empty = True
-        for entry in old_node.get_entries():
-            is_empty = False
-            break
-        if is_empty:
-            continue
-
-        # For every target platform, check whether there's a build
-        # of this revision
-        for platform in TargetPlatform.select(env, config.name, db=db):
-            builds = list(Build.select(env, config.name, rev, platform.id,
-                                       db=db))
-            if builds:
-                build = builds[0]
-            else:
-                build = None
-
-            yield platform, rev, build
-
-
-class BuildQueue(object):
-    """Enapsulates the build queue of an environment.
-    
-    A build queue manages the the registration of build slaves and detection of
-    repository revisions that need to be built.
-    """
-
-    def __init__(self, env, build_all=False, stabilize_wait=0, timeout=0):
-        """Create the build queue.
-        
-        :param env: the Trac environment
-        :param build_all: whether older revisions should be built
-        :param stabilize_wait: The time in seconds to wait before considering
-                        the repository stable to create a build in the queue.
-        :param timeout: the time in seconds after which an in-progress build
-                        should be considered orphaned, and reset to pending
-                        state
-        """
-        self.env = env
-        self.log = env.log
-        self.build_all = build_all
-        self.stabilize_wait = stabilize_wait
-        self.timeout = timeout
-
-    # Build scheduling
-
-    def get_build_for_slave(self, name, properties):
-        """Check whether one of the pending builds can be built by the build
-        slave.
-        
-        :param name: the name of the slave
-        :type name: `basestring`
-        :param properties: the slave configuration
-        :type properties: `dict`
-        :return: the allocated build, or `None` if no build was found
-        :rtype: `Build`
-        """
-        log.debug('Checking for pending builds...')
-
-        db = self.env.get_db_cnx()
-        repos = self.env.get_repository()
-
-        self.reset_orphaned_builds()
-
-        # Iterate through pending builds by descending revision timestamp, to
-        # avoid the first configuration/platform getting all the builds
-        platforms = [p.id for p in self.match_slave(name, properties)]
-        build = None
-        builds_to_delete = []
-        for build in Build.select(self.env, status=Build.PENDING, db=db):
-            if self.should_delete_build(build, repos):
-                self.log.info('Scheduling build %d for deletion', build.id)
-                builds_to_delete.append(build)
-            elif build.platform in platforms:
-                break
-        else:
-            self.log.debug('No pending builds.')
-            build = None
-
-        # delete any obsolete builds
-        for build_to_delete in builds_to_delete:
-            build_to_delete.delete(db=db)
-
-        if build:
-            build.slave = name
-            build.slave_info.update(properties)
-            build.status = Build.IN_PROGRESS
-            build.update(db=db)
-
-        if build or builds_to_delete:
-            db.commit()
-
-        return build
-
-    def match_slave(self, name, properties):
-        """Match a build slave against available target platforms.
-        
-        :param name: the name of the slave
-        :type name: `basestring`
-        :param properties: the slave configuration
-        :type properties: `dict`
-        :return: the list of platforms the slave matched
-        """
-        platforms = []
-
-        for config in BuildConfig.select(self.env):
-            for platform in TargetPlatform.select(self.env, config=config.name):
-                match = True
-                for propname, pattern in ifilter(None, platform.rules):
-                    try:
-                        propvalue = properties.get(propname)
-                        if not propvalue or not re.match(pattern, propvalue):
-                            match = False
-                            break
-                    except re.error:
-                        self.log.error('Invalid platform matching pattern "%s"',
-                                       pattern, exc_info=True)
-                        match = False
-                        break
-                if match:
-                    self.log.debug('Slave %r matched target platform %r of '
-                                   'build configuration %r', name,
-                                   platform.name, config.name)
-                    platforms.append(platform)
-
-        if not platforms:
-            self.log.warning('Slave %r matched none of the target platforms',
-                             name)
-
-        return platforms
-
-    def populate(self):
-        """Add a build for the next change on each build configuration to the
-        queue.
-
-        The next change is the latest repository check-in for which there isn't
-        a corresponding build on each target platform. Repeatedly calling this
-        method will eventually result in the entire change history of the build
-        configuration being in the build queue.
-        """
-        repos = self.env.get_repository()
-        if hasattr(repos, 'sync'):
-            repos.sync()
-
-        db = self.env.get_db_cnx()
-        builds = []
-
-        for config in BuildConfig.select(self.env, db=db):
-            platforms = []
-            for platform, rev, build in collect_changes(repos, config, db):
-
-                if not self.build_all and platform.id in platforms:
-                    # We've seen this platform already, so these are older
-                    # builds that should only be built if built_all=True
-                    self.log.debug('Ignoring older revisions for configuration '
-                                   '%r on %r', config.name, platform.name)
-                    break
-
-                platforms.append(platform.id)
-
-                if build is None:
-                    self.log.info('Enqueuing build of configuration "%s" at '
-                                  'revision [%s] on %s', config.name, rev,
-                                  platform.name)
-
-                    rev_time = repos.get_changeset(rev).date
-                    if isinstance(rev_time, datetime): # Trac>=0.11
-                        from trac.util.datefmt import to_timestamp
-                        rev_time = to_timestamp(rev_time)
-                    age = int(time.time()) - rev_time
-                    if self.stabilize_wait and age < self.stabilize_wait:
-                        self.log.info('Delaying build of revision %s until %s '
-                                      'seconds pass. Current age is: %s '
-                                      'seconds' % (rev, self.stabilize_wait,
-                                      age))
-                        continue
-
-                    build = Build(self.env, config=config.name,
-                                  platform=platform.id, rev=str(rev),
-                                  rev_time=rev_time)
-                    builds.append(build)
-
-        for build in builds:
-            build.insert(db=db)
-
-        db.commit()
-
-    def reset_orphaned_builds(self):
-        """Reset all in-progress builds to ``PENDING`` state if they've been
-        running so long that the configured timeout has been reached.
-        
-        This is used to cleanup after slaves that have unexpectedly cancelled
-        a build without notifying the master, or are for some other reason not
-        reporting back status updates.
-        """
-        if not self.timeout:
-            # If no timeout is set, none of the in-progress builds can be
-            # considered orphaned
-            return
-
-        db = self.env.get_db_cnx()
-        now = int(time.time())
-        for build in Build.select(self.env, status=Build.IN_PROGRESS, db=db):
-            if now - build.started < self.timeout:
-                # This build has not reached the timeout yet, assume it's still
-                # being executed
-                # FIXME: ideally, we'd base this check on the last activity on
-                #        the build, not the start time
-                continue
-            build.status = Build.PENDING
-            build.slave = None
-            build.slave_info = {}
-            build.started = 0
-            for step in list(BuildStep.select(self.env, build=build.id, db=db)):
-                step.delete(db=db)
-            build.update(db=db)
-        db.commit()
-
-    def should_delete_build(self, build, repos):
-        # Ignore pending builds for deactived build configs
-        config = BuildConfig.fetch(self.env, build.config)
-        if not config.active:
-            log.info('Dropping build of configuration "%s" at '
-                     'revision [%s] on "%s" because the configuration is '
-                     'deactivated', config.name, build.rev,
-                     TargetPlatform.fetch(self.env, build.platform).name)
-            return True
-
-        # Stay within the revision limits of the build config
-        if (config.min_rev and repos.rev_older_than(build.rev,
-                                                    config.min_rev)) \
-        or (config.max_rev and repos.rev_older_than(config.max_rev,
-                                                    build.rev)):
-            # This minimum and/or maximum revision has changed since
-            # this build was enqueued, so drop it
-            log.info('Dropping build of configuration "%s" at revision [%s] on '
-                     '"%s" because it is outside of the revision range of the '
-                     'configuration', config.name, build.rev,
-                     TargetPlatform.fetch(self.env, build.platform).name)
-            return True
-
-        return False
deleted file mode 100644
--- a/trac-0.11/bitten/recipe.py
+++ /dev/null
@@ -1,278 +0,0 @@
-# -*- coding: utf-8 -*-
-#
-# Copyright (C) 2007 Edgewall Software
-# Copyright (C) 2005-2007 Christopher Lenz <cmlenz@gmx.de>
-# 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.
-
-"""Execution of build recipes.
-
-This module provides various classes that can be used to process build recipes,
-most importantly the `Recipe` class.
-"""
-
-import keyword
-import logging
-import os
-try:
-    set
-except NameError:
-    from sets import Set as set
-
-from pkg_resources import WorkingSet
-from bitten.build import BuildError
-from bitten.build.config import Configuration
-from bitten.util import xmlio
-
-__all__ = ['Context', 'Recipe', 'Step', 'InvalidRecipeError']
-__docformat__ = 'restructuredtext en'
-
-log = logging.getLogger('bitten.recipe')
-
-
-class InvalidRecipeError(Exception):
-    """Exception raised when a recipe is not valid."""
-
-
-class Context(object):
-    """The context in which a build is executed."""
-
-    step = None # The current step
-    generator = None # The current generator (namespace#name)
-
-    def __init__(self, basedir, config=None, vars=None):
-        """Initialize the context.
-        
-        :param basedir: a string containing the working directory for the build.
-                        (may be a pattern for replacement ex: 'build_${build}'
-        :param config: the build slave configuration
-        :type config: `Configuration`
-        """        
-        self.config = config or Configuration()
-        self.vars = vars or {}
-        self.output = []
-        self.basedir = os.path.realpath(self.config.interpolate(basedir,
-                                                                **self.vars))
-
-    def run(self, step, namespace, name, attr):
-        """Run the specified recipe command.
-        
-        :param step: the build step that the command belongs to
-        :param namespace: the namespace URI of the command
-        :param name: the local tag name of the command
-        :param attr: a dictionary containing the attributes defined on the
-                     command element
-        """
-        self.step = step
-
-        try:
-            function = None
-            qname = '#'.join(filter(None, [namespace, name]))
-            if namespace:
-                group = 'bitten.recipe_commands'
-                for entry_point in WorkingSet().iter_entry_points(group, qname):
-                    function = entry_point.load()
-                    break
-            elif name == 'report':
-                function = Context.report_file
-            if not function:
-                raise InvalidRecipeError('Unknown recipe command %s' % qname)
-
-            def escape(name):
-                name = name.replace('-', '_')
-                if keyword.iskeyword(name) or name in __builtins__:
-                    name = name + '_'
-                return name
-            args = dict([(escape(name),
-                          self.config.interpolate(attr[name], **self.vars))
-                         for name in attr])
-
-            self.generator = qname
-            log.debug('Executing %s with arguments: %s', function, args)
-            function(self, **args)
-
-        finally:
-            self.generator = None
-            self.step = None
-
-    def error(self, message):
-        """Record an error message.
-        
-        :param message: a string containing the error message.
-        """
-        self.output.append((Recipe.ERROR, None, self.generator, message))
-
-    def log(self, xml):
-        """Record log output.
-        
-        :param xml: an XML fragment containing the log messages
-        """
-        self.output.append((Recipe.LOG, None, self.generator, xml))
-
-    def report(self, category, xml):
-        """Record report data.
-        
-        :param category: the name of category of the report
-        :param xml: an XML fragment containing the report data
-        """
-        self.output.append((Recipe.REPORT, category, self.generator, xml))
-
-    def report_file(self, category=None, file_=None):
-        """Read report data from a file and record it.
-        
-        :param category: the name of the category of the report
-        :param file\_: the path to the file containing the report data, relative
-                       to the base directory
-        """
-        filename = self.resolve(file_)
-        try:
-            fileobj = file(filename, 'r')
-            try:
-                xml_elem = xmlio.Fragment()
-                for child in xmlio.parse(fileobj).children():
-                    child_elem = xmlio.Element(child.name, **dict([
-                        (name, value) for name, value in child.attr.items()
-                        if value is not None
-                    ]))
-                    xml_elem.append(child_elem[
-                        [xmlio.Element(grandchild.name)[grandchild.gettext()]
-                        for grandchild in child.children()]
-                    ])
-                self.output.append((Recipe.REPORT, category, None, xml_elem))
-            finally:
-                fileobj.close()
-        except xmlio.ParseError, e:
-            self.error('Failed to parse %s report at %s: %s'
-                       % (category, filename, e))
-        except IOError, e:
-            self.error('Failed to read %s report at %s: %s'
-                       % (category, filename, e))
-
-    def resolve(self, *path):
-        """Return the path of a file relative to the base directory.
-        
-        Accepts any number of positional arguments, which are joined using the
-        system path separator to form the path.
-        """
-        return os.path.normpath(os.path.join(self.basedir, *path))
-
-
-class Step(object):
-    """Represents a single step of a build recipe.
-
-    Iterate over an object of this class to get the commands to execute, and
-    their keyword arguments.
-    """
-
-    def __init__(self, elem):
-        """Create the step.
-        
-        :param elem: the XML element representing the step
-        :type elem: `ParsedElement`
-        """
-        self._elem = elem
-        self.id = elem.attr['id']
-        self.description = elem.attr.get('description')
-        self.onerror = elem.attr.get('onerror', 'fail')
-
-    def __repr__(self):
-        return '<%s %r>' % (type(self).__name__, self.id)
-
-    def execute(self, ctxt):
-        """Execute this step in the given context.
-        
-        :param ctxt: the build context
-        :type ctxt: `Context`
-        """
-        for child in self._elem:
-            ctxt.run(self, child.namespace, child.name, child.attr)
-
-        errors = []
-        while ctxt.output:
-            type, category, generator, output = ctxt.output.pop(0)
-            yield type, category, generator, output
-            if type == Recipe.ERROR:
-                errors.append((generator, output))
-        if errors:
-            if self.onerror != 'ignore':
-                raise BuildError('Build step %s failed' % self.id)
-            log.warning('Continuing despite errors in step %s (%s)', self.id,
-                        ', '.join([error[1] for error in errors]))
-
-
-class Recipe(object):
-    """A build recipe.
-    
-    Iterate over this object to get the individual build steps in the order
-    they have been defined in the recipe file.
-    """
-
-    ERROR = 'error'
-    LOG = 'log'
-    REPORT = 'report'
-
-    def __init__(self, xml, basedir=os.getcwd(), config=None):
-        """Create the recipe.
-        
-        :param xml: the XML document representing the recipe
-        :type xml: `ParsedElement`
-        :param basedir: the base directory for the build
-        :param config: the slave configuration (optional)
-        :type config: `Configuration`
-        """
-        assert isinstance(xml, xmlio.ParsedElement)
-        vars = dict([(name, value) for name, value in xml.attr.items()
-                     if not name.startswith('xmlns')])
-        self.ctxt = Context(basedir, config, vars)
-        self._root = xml
-
-    def __iter__(self):
-        """Iterate over the individual steps of the recipe."""
-        for child in self._root.children('step'):
-            yield Step(child)
-
-    def validate(self):
-        """Validate the recipe.
-        
-        This method checks a number of constraints:
-         - the name of the root element must be "build"
-         - the only permitted child elements or the root element with the name
-           "step"
-         - the recipe must contain at least one step
-         - step elements must have a unique "id" attribute
-         - a step must contain at least one nested command
-         - commands must not have nested content
-
-        :raise InvalidRecipeError: in case any of the above contraints is
-                                   violated
-        """
-        if self._root.name != 'build':
-            raise InvalidRecipeError('Root element must be <build>')
-        steps = list(self._root.children())
-        if not steps:
-            raise InvalidRecipeError('Recipe defines no build steps')
-
-        step_ids = set()
-        for step in steps:
-            if step.name != 'step':
-                raise InvalidRecipeError('Only <step> elements allowed at '
-                                         'top level of recipe')
-            if not step.attr.get('id'):
-                raise InvalidRecipeError('Steps must have an "id" attribute')
-
-            if step.attr['id'] in step_ids:
-                raise InvalidRecipeError('Duplicate step ID "%s"' %
-                                         step.attr['id'])
-            step_ids.add(step.attr['id'])
-
-            cmds = list(step.children())
-            if not cmds:
-                raise InvalidRecipeError('Step "%s" has no recipe commands' %
-                                         step.attr['id'])
-            for cmd in cmds:
-                if len(list(cmd.children())):
-                    raise InvalidRecipeError('Recipe command <%s> has nested '
-                                             'content' % cmd.name)
deleted file mode 100644
--- a/trac-0.11/bitten/report/__init__.py
+++ /dev/null
@@ -1,10 +0,0 @@
-# -*- coding: utf-8 -*-
-#
-# 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.
-
-__docformat__ = 'restructuredtext en'
deleted file mode 100644
--- a/trac-0.11/bitten/report/coverage.py
+++ /dev/null
@@ -1,213 +0,0 @@
-# -*- 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.
-
-from trac.core import *
-from trac.mimeview.api import IHTMLPreviewAnnotator
-from trac.web.chrome import Chrome, add_stylesheet
-from trac.web.clearsilver import HDFWrapper
-from bitten.api import IReportChartGenerator, IReportSummarizer
-from bitten.model import BuildConfig, Build, Report
-
-__docformat__ = 'restructuredtext en'
-
-
-class TestCoverageChartGenerator(Component):
-    implements(IReportChartGenerator)
-
-    # IReportChartGenerator methods
-
-    def get_supported_categories(self):
-        return ['coverage']
-
-    def generate_chart_data(self, req, config, category):
-        assert category == 'coverage'
-
-        db = self.env.get_db_cnx()
-        cursor = db.cursor()
-        cursor.execute("""
-SELECT build.rev, SUM(%s) AS loc, SUM(%s * %s / 100) AS cov
-FROM bitten_build AS build
- LEFT OUTER JOIN bitten_report AS report ON (report.build=build.id)
- LEFT OUTER JOIN bitten_report_item AS item_lines
-  ON (item_lines.report=report.id AND item_lines.name='lines')
- LEFT OUTER JOIN bitten_report_item AS item_percentage
-  ON (item_percentage.report=report.id AND item_percentage.name='percentage' AND
-      item_percentage.item=item_lines.item)
-WHERE build.config=%%s AND report.category='coverage'
-GROUP BY build.rev_time, build.rev, build.platform
-ORDER BY build.rev_time""" % (db.cast('item_lines.value', 'int'),
-                              db.cast('item_lines.value', 'int'),
-                              db.cast('item_percentage.value', 'int')),
-                              (config.name,))
-
-        prev_rev = None
-        coverage = []
-        for rev, loc, cov in cursor:
-            if rev != prev_rev:
-                coverage.append([rev, 0, 0])
-            if loc > coverage[-1][1]:
-                coverage[-1][1] = int(loc)
-            if cov > coverage[-1][2]:
-                coverage[-1][2] = int(cov)
-            prev_rev = rev
-
-        data = {'title': 'Test Coverage',
-                'data': [
-                    [''] + ['[%s]' % item[0] for item in coverage],
-                    ['Lines of code'] + [item[1] for item in coverage],
-                    ['Coverage'] + [int(item[2]) for item in coverage]
-                ]}
-
-        return 'bitten_chart_coverage.html', data
-
-
-class TestCoverageSummarizer(Component):
-    implements(IReportSummarizer)
-
-    # IReportSummarizer methods
-
-    def get_supported_categories(self):
-        return ['coverage']
-
-    def render_summary(self, req, config, build, step, category):
-        assert category == 'coverage'
-
-        db = self.env.get_db_cnx()
-        cursor = db.cursor()
-        cursor.execute("""
-SELECT item_name.value AS unit, item_file.value AS file,
-       max(item_lines.value) AS loc, max(item_percentage.value) AS cov
-FROM bitten_report AS report
- LEFT OUTER JOIN bitten_report_item AS item_name
-  ON (item_name.report=report.id AND item_name.name='name')
- LEFT OUTER JOIN bitten_report_item AS item_file
-  ON (item_file.report=report.id AND item_file.item=item_name.item AND
-      item_file.name='file')
- LEFT OUTER JOIN bitten_report_item AS item_lines
-  ON (item_lines.report=report.id AND item_lines.item=item_name.item AND
-      item_lines.name='lines')
- LEFT OUTER JOIN bitten_report_item AS item_percentage
-  ON (item_percentage.report=report.id AND
-      item_percentage.item=item_name.item AND
-      item_percentage.name='percentage')
-WHERE category='coverage' AND build=%s AND step=%s
-GROUP BY file, item_name.value
-ORDER BY item_name.value""", (build.id, step.name))
-
-        units = []
-        total_loc, total_cov = 0, 0
-        for unit, file, loc, cov in cursor:
-            try:
-                loc, cov = int(loc), float(cov)
-            except TypeError:
-                continue # no rows
-            if loc:
-                d = {'name': unit, 'loc': loc, 'cov': int(cov)}
-                if file:
-                    d['href'] = req.href.browser(config.path, file, rev=build.rev, annotate='coverage')
-                units.append(d)
-                total_loc += loc
-                total_cov += loc * cov
-
-        coverage = 0
-        if total_loc != 0:
-            coverage = total_cov // total_loc
-
-        return 'bitten_summary_coverage.html', {
-            'units': units,
-            'totals': {'loc': total_loc, 'cov': int(coverage)}
-        }
-
-
-# Coverage annotation requires the new interface from 0.11
-if hasattr(IHTMLPreviewAnnotator, 'get_annotation_data'):
-    class TestCoverageAnnotator(Component):
-        """
-        >>> from genshi.builder import tag
-        >>> from trac.test import Mock, MockPerm
-        >>> from trac.mimeview import Context
-        >>> from trac.web.href import Href
-        >>> from bitten.model import BuildConfig, Build, Report
-        >>> from bitten.report.tests.coverage import env_stub_with_tables
-        >>> env = env_stub_with_tables()
-
-        >>> BuildConfig(env, name='trunk', path='trunk').insert()
-        >>> Build(env, rev=123, config='trunk', rev_time=12345, platform=1).insert()
-        >>> rpt = Report(env, build=1, step='test', category='coverage')
-        >>> rpt.items.append({'file': 'foo.py', 'line_hits': '5 - 0'})
-        >>> rpt.insert()
-
-        >>> ann = TestCoverageAnnotator(env)
-        >>> req = Mock(href=Href('/'), perm=MockPerm(), chrome={})
-
-        Version in the branch should not match:
-        >>> context = Context.from_request(req, 'source', 'branches/blah/foo.py', 123)
-        >>> ann.get_annotation_data(context)
-        []
-
-        Version in the trunk should match:
-        >>> context = Context.from_request(req, 'source', 'trunk/foo.py', 123)
-        >>> data = ann.get_annotation_data(context)
-        >>> print data
-        [u'5', u'-', u'0']
-
-        >>> def annotate_row(lineno, line):
-        ...     row = tag.tr()
-        ...     ann.annotate_row(context, row, lineno, line, data)
-        ...     return row.generate().render('html')
-
-        >>> annotate_row(1, 'x = 1')
-        '<tr><th class="covered">5</th></tr>'
-        >>> annotate_row(2, '')
-        '<tr><th></th></tr>'
-        >>> annotate_row(3, 'y = x')
-        '<tr><th class="uncovered">0</th></tr>'
-        """
-        implements(IHTMLPreviewAnnotator)
-
-        # IHTMLPreviewAnnotator methods
-
-        def get_annotation_type(self):
-            return 'coverage', 'Cov', 'Code coverage'
-
-        def get_annotation_data(self, context):
-            add_stylesheet(context.req, 'bitten/bitten_coverage.css')
-
-            resource = context.resource
-            builds = Build.select(self.env, rev=resource.version)
-            reports = []
-            for build in builds:
-                config = BuildConfig.fetch(self.env, build.config)
-                if not resource.id.startswith(config.path):
-                    continue
-                reports = Report.select(self.env, build=build.id,
-                                        category='coverage')
-                path_in_config = resource.id[len(config.path):].lstrip('/')
-                for report in reports:
-                    for item in report.items:
-                        if item.get('file') == path_in_config:
-                            # TODO should aggregate coverage across builds
-                            return item.get('line_hits', '').split()
-            return []
-
-        def annotate_row(self, context, row, lineno, line, data):
-            self.log.debug('%s', data)
-            from genshi.builder import tag
-            lineno -= 1 # 0-based index for data
-            if lineno >= len(data):
-                row.append(tag.th())
-                return
-            row_data = data[lineno]
-            if row_data == '-':
-                row.append(tag.th())
-            elif row_data == '0':
-                row.append(tag.th(row_data, class_='uncovered'))
-            else:
-                row.append(tag.th(row_data, class_='covered'))
deleted file mode 100644
--- a/trac-0.11/bitten/report/testing.py
+++ /dev/null
@@ -1,122 +0,0 @@
-# -*- 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.
-
-from trac.core import *
-from trac.web.chrome import Chrome
-from trac.web.clearsilver import HDFWrapper
-from bitten.api import IReportChartGenerator, IReportSummarizer
-
-__docformat__ = 'restructuredtext en'
-
-
-class TestResultsChartGenerator(Component):
-    implements(IReportChartGenerator)
-
-    # IReportChartGenerator methods
-
-    def get_supported_categories(self):
-        return ['test']
-
-    def generate_chart_data(self, req, config, category):
-        assert category == 'test'
-
-        db = self.env.get_db_cnx()
-        cursor = db.cursor()
-        cursor.execute("""
-SELECT build.rev, build.platform, item_status.value AS status, COUNT(*) AS num
-FROM bitten_build AS build
- LEFT OUTER JOIN bitten_report AS report ON (report.build=build.id)
- LEFT OUTER JOIN bitten_report_item AS item_status
-  ON (item_status.report=report.id AND item_status.name='status')
-WHERE build.config=%s AND report.category='test'
-GROUP BY build.rev_time, build.rev, build.platform, item_status.value
-ORDER BY build.rev_time, build.platform""", (config.name,))
-
-        prev_rev = None
-        prev_platform, platform_total = None, 0
-        tests = []
-        for rev, platform, status, num in cursor:
-            if rev != prev_rev:
-                tests.append([rev, 0, 0])
-                prev_rev = rev
-                platform_total = 0
-            if platform != prev_platform:
-                prev_platform = platform
-                platform_total = 0
-
-            platform_total += num
-            tests[-1][1] = max(platform_total, tests[-1][1])
-            if status != 'success':
-                tests[-1][2] = max(num, tests[-1][2])
-
-        data = {'title': 'Unit Tests',
-                'data': [
-                    [''] + ['[%s]' % item[0] for item in tests],
-                    ['Total'] + [item[1] for item in tests],
-                    ['Failures'] + [item[2] for item in tests]
-                ]}
-
-        return 'bitten_chart_tests.html', data
-
-
-class TestResultsSummarizer(Component):
-    implements(IReportSummarizer)
-
-    # IReportSummarizer methods
-
-    def get_supported_categories(self):
-        return ['test']
-
-    def render_summary(self, req, config, build, step, category):
-        assert category == 'test'
-
-        db = self.env.get_db_cnx()
-        cursor = db.cursor()
-        cursor.execute("""
-SELECT item_fixture.value AS fixture, item_file.value AS file,
-       COUNT(item_success.value) AS num_success,
-       COUNT(item_failure.value) AS num_failure,
-       COUNT(item_error.value) AS num_error
-FROM bitten_report AS report
- LEFT OUTER JOIN bitten_report_item AS item_fixture
-  ON (item_fixture.report=report.id AND item_fixture.name='fixture')
- LEFT OUTER JOIN bitten_report_item AS item_file
-  ON (item_file.report=report.id AND item_file.item=item_fixture.item AND
-      item_file.name='file')
- LEFT OUTER JOIN bitten_report_item AS item_success
-  ON (item_success.report=report.id AND item_success.item=item_fixture.item AND
-      item_success.name='status' AND item_success.value='success')
- LEFT OUTER JOIN bitten_report_item AS item_failure
-  ON (item_failure.report=report.id AND item_failure.item=item_fixture.item AND
-      item_failure.name='status' AND item_failure.value='failure')
- LEFT OUTER JOIN bitten_report_item AS item_error
-  ON (item_error.report=report.id AND item_error.item=item_fixture.item AND
-      item_error.name='status' AND item_error.value='error')
-WHERE category='test' AND build=%s AND step=%s
-GROUP BY file, fixture
-ORDER BY fixture""", (build.id, step.name))
-
-        fixtures = []
-        total_success, total_failure, total_error = 0, 0, 0
-        for fixture, file, num_success, num_failure, num_error in cursor:
-            fixtures.append({'name': fixture, 'num_success': num_success,
-                             'num_error': num_error,
-                             'num_failure': num_failure})
-            total_success += num_success
-            total_failure += num_failure
-            total_error += num_error
-            if file:
-                fixtures[-1]['href'] = req.href.browser(config.path, file)
-
-        data = {'fixtures': fixtures,
-                'totals': {'success': total_success, 'failure': total_failure,
-                           'error': total_error}
-               }
-        return 'bitten_summary_tests.html', data
deleted file mode 100644
--- a/trac-0.11/bitten/report/tests/__init__.py
+++ /dev/null
@@ -1,22 +0,0 @@
-# -*- 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 bitten.report.tests import coverage, testing
-
-def suite():
-    suite = unittest.TestSuite()
-    suite.addTest(coverage.suite())
-    suite.addTest(testing.suite())
-    return suite
-
-if __name__ == '__main__':
-    unittest.main(defaultTest='suite')
deleted file mode 100644
--- a/trac-0.11/bitten/report/tests/coverage.py
+++ /dev/null
@@ -1,110 +0,0 @@
-# -*- 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 doctest
-import unittest
-
-from trac.db import DatabaseManager
-from trac.test import EnvironmentStub, Mock
-from bitten.model import *
-from bitten.report import coverage
-from bitten.report.coverage import TestCoverageChartGenerator
-
-def env_stub_with_tables():
-    env = EnvironmentStub()
-    db = env.get_db_cnx()
-    cursor = db.cursor()
-    connector, _ = DatabaseManager(env)._get_connector()
-    for table in schema:
-        for stmt in connector.to_sql(table):
-            cursor.execute(stmt)
-    return env
-
-class TestCoverageChartGeneratorTestCase(unittest.TestCase):
-
-    def setUp(self):
-        self.env = env_stub_with_tables()
-        self.env.path = ''
-
-    def test_supported_categories(self):
-        generator = TestCoverageChartGenerator(self.env)
-        self.assertEqual(['coverage'], generator.get_supported_categories())
-
-    def test_no_reports(self):
-        req = Mock()
-        config = Mock(name='trunk')
-        generator = TestCoverageChartGenerator(self.env)
-        template, data = generator.generate_chart_data(req, config, 'coverage')
-        self.assertEqual('bitten_chart_coverage.html', template)
-        self.assertEqual('Test Coverage', data['title'])
-        self.assertEqual('', data['data'][0][0])
-        self.assertEqual('Lines of code', data['data'][1][0])
-        self.assertEqual('Coverage', data['data'][2][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='coverage')
-        report.items += [{'lines': '12', 'percentage': '25'}]
-        report.insert()
-
-        req = Mock()
-        generator = TestCoverageChartGenerator(self.env)
-        template, data = generator.generate_chart_data(req, config, 'coverage')
-        self.assertEqual('bitten_chart_coverage.html', template)
-        self.assertEqual('Test Coverage', data['title'])
-        self.assertEqual('', data['data'][0][0])
-        self.assertEqual('[123]', data['data'][0][1])
-        self.assertEqual('Lines of code', data['data'][1][0])
-        self.assertEqual(12, data['data'][1][1])
-        self.assertEqual('Coverage', data['data'][2][0])
-        self.assertEqual(3, data['data'][2][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='coverage')
-        report.items += [{'lines': '12', 'percentage': '25'}]
-        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='coverage')
-        report.items += [{'lines': '12', 'percentage': '50'}]
-        report.insert()
-
-        req = Mock()
-        generator = TestCoverageChartGenerator(self.env)
-        template, data = generator.generate_chart_data(req, config, 'coverage')
-        self.assertEqual('bitten_chart_coverage.html', template)
-        self.assertEqual('Test Coverage', data['title'])
-        self.assertEqual('', data['data'][0][0])
-        self.assertEqual('[123]', data['data'][0][1])
-        self.assertEqual('Lines of code', data['data'][1][0])
-        self.assertEqual(12, data['data'][1][1])
-        self.assertEqual('Coverage', data['data'][2][0])
-        self.assertEqual(6, data['data'][2][1])
-
-
-def suite():
-    suite = unittest.TestSuite()
-    suite.addTest(unittest.makeSuite(TestCoverageChartGeneratorTestCase))
-    suite.addTest(doctest.DocTestSuite(coverage))
-    return suite
-
-if __name__ == '__main__':
-    unittest.main(defaultTest='suite')
deleted file mode 100644
--- a/trac-0.11/bitten/report/tests/testing.py
+++ /dev/null
@@ -1,107 +0,0 @@
-# -*- 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.testing import TestResultsChartGenerator
-
-
-class TestResultsChartGeneratorTestCase(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 = TestResultsChartGenerator(self.env)
-        self.assertEqual(['test'], generator.get_supported_categories())
-
-    def test_no_reports(self):
-        req = Mock()
-        config = Mock(name='trunk')
-        generator = TestResultsChartGenerator(self.env)
-        template, data = generator.generate_chart_data(req, config, 'test')
-        self.assertEqual('bitten_chart_tests.html', template)
-        self.assertEqual('Unit Tests', data['title'])
-        self.assertEqual('', data['data'][0][0])
-        self.assertEqual('Total', data['data'][1][0])
-        self.assertEqual('Failures', data['data'][2][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='test')
-        report.items += [{'status': 'success'}, {'status': 'failure'},
-                         {'status': 'success'}]
-        report.insert()
-
-        req = Mock()
-        generator = TestResultsChartGenerator(self.env)
-        template, data = generator.generate_chart_data(req, config, 'test')
-        self.assertEqual('bitten_chart_tests.html', template)
-        self.assertEqual('Unit Tests', data['title'])
-        self.assertEqual('', data['data'][0][0])
-        self.assertEqual('[123]', data['data'][0][1])
-        self.assertEqual('Total', data['data'][1][0])
-        self.assertEqual(3, data['data'][1][1])
-        self.assertEqual('Failures', data['data'][2][0])
-        self.assertEqual(1, data['data'][2][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='test')
-        report.items += [{'status': 'success'}, {'status': 'failure'},
-                         {'status': 'success'}]
-        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='test')
-        report.items += [{'status': 'success'}, {'status': 'failure'},
-                         {'status': 'failure'}]
-        report.insert()
-
-        req = Mock()
-        generator = TestResultsChartGenerator(self.env)
-        template, data = generator.generate_chart_data(req, config, 'test')
-        self.assertEqual('bitten_chart_tests.html', template)
-        self.assertEqual('Unit Tests', data['title'])
-        self.assertEqual('', data['data'][0][0])
-        self.assertEqual('[123]', data['data'][0][1])
-        self.assertEqual('Total', data['data'][1][0])
-        self.assertEqual(3, data['data'][1][1])
-        self.assertEqual('Failures', data['data'][2][0])
-        self.assertEqual(2, data['data'][2][1])
-
-
-def suite():
-    suite = unittest.TestSuite()
-    suite.addTest(unittest.makeSuite(TestResultsChartGeneratorTestCase))
-    return suite
-
-if __name__ == '__main__':
-    unittest.main(defaultTest='suite')
deleted file mode 100755
--- a/trac-0.11/bitten/slave.py
+++ /dev/null
@@ -1,410 +0,0 @@
-# -*- coding: utf-8 -*-
-#
-# Copyright (C) 2007 Edgewall Software
-# Copyright (C) 2005-2007 Christopher Lenz <cmlenz@gmx.de>
-# 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.
-
-"""Implementation of the build slave."""
-
-from datetime import datetime
-import errno
-import urllib2
-import logging
-import os
-import platform
-import shutil
-import socket
-import tempfile
-import time
-
-from bitten.build import BuildError
-from bitten.build.config import Configuration
-from bitten.recipe import Recipe
-from bitten.util import xmlio
-
-EX_OK = getattr(os, "EX_OK", 0)
-EX_UNAVAILABLE = getattr(os, "EX_UNAVAILABLE", 69)
-EX_PROTOCOL = getattr(os, "EX_PROTOCOL", 76)
-
-__all__ = ['BuildSlave', 'ExitSlave']
-__docformat__ = 'restructuredtext en'
-
-log = logging.getLogger('bitten.slave')
-
-# List of network errors which are usually temporary and non critical.
-temp_net_errors = [errno.ENETUNREACH, errno.ENETDOWN, errno.ETIMEDOUT,
-                   errno.ECONNREFUSED]
-
-def _rmtree(root):
-    """Catch shutil.rmtree failures on Windows when files are read-only."""
-    def _handle_error(fn, path, excinfo):
-       os.chmod(path, 0666)
-       fn(path)
-    return shutil.rmtree(root, onerror=_handle_error) 
-
-class SaneHTTPErrorProcessor(urllib2.HTTPErrorProcessor):
-    "The HTTPErrorProcessor defined in urllib needs some love."
-
-    def http_response(self, request, response):
-        code, msg, hdrs = response.code, response.msg, response.info()
-        if code >= 300:
-            response = self.parent.error(
-                'http', request, response, code, msg, hdrs)
-        return response
-
-
-class SaneHTTPRequest(urllib2.Request):
-
-    def __init__(self, method, url, data=None, headers={}):
-        urllib2.Request.__init__(self, url, data, headers)
-        self.method = method
-
-    def get_method(self):
-        if self.method is None:
-            self.method = self.has_data() and 'POST' or 'GET'
-        return self.method
-
-
-class BuildSlave(object):
-    """BEEP initiator implementation for the build slave."""
-
-    def __init__(self, urls, name=None, config=None, dry_run=False,
-                 work_dir=None, build_dir="build_${build}",
-                 keep_files=False, single_build=False,
-                 poll_interval=300, username=None, password=None,
-                 dump_reports=False):
-        """Create the build slave instance.
-        
-        :param urls: a list of URLs of the build masters to connect to, or a
-                     single-element list containing the path to a build recipe
-                     file
-        :param name: the name with which this slave should identify itself
-        :param config: the path to the slave configuration file
-        :param dry_run: wether the build outcome should not be reported back
-                        to the master
-        :param work_dir: the working directory to use for build execution
-        :param build_dir: the pattern to use for naming the build subdir
-        :param keep_files: whether files and directories created for build
-                           execution should be kept when done
-        :param single_build: whether this slave should exit after completing a 
-                             single build, or continue processing builds forever
-        :param poll_interval: the time in seconds to wait between requesting
-                              builds from the build master (default is five
-                              minutes)
-        :param username: the username to use when authentication against the
-                         build master is requested
-        :param password: the password to use when authentication is needed
-        :param dump_reports: whether report data should be written to the
-                             standard output, in addition to being transmitted
-                             to the build master
-        """
-        self.urls = urls
-        self.local = len(urls) == 1 and not urls[0].startswith('http://') \
-                                    and not urls[0].startswith('https://')
-        if name is None:
-            name = platform.node().split('.', 1)[0].lower()
-        self.name = name
-        self.config = Configuration(config)
-        self.dry_run = dry_run
-        if not work_dir:
-            work_dir = tempfile.mkdtemp(prefix='bitten')
-        elif not os.path.exists(work_dir):
-            os.makedirs(work_dir)
-        self.work_dir = work_dir
-        self.build_dir = build_dir
-        self.keep_files = keep_files
-        self.single_build = single_build
-        self.poll_interval = poll_interval
-        self.dump_reports = dump_reports
-
-        if not self.local:
-            self.opener = urllib2.build_opener(SaneHTTPErrorProcessor)
-            password_mgr = urllib2.HTTPPasswordMgrWithDefaultRealm()
-            if username and password:
-                log.debug('Enabling authentication with username %r', username)
-                password_mgr.add_password(None, urls, username, password)
-            self.opener.add_handler(urllib2.HTTPBasicAuthHandler(password_mgr))
-            self.opener.add_handler(urllib2.HTTPDigestAuthHandler(password_mgr))
-
-    def request(self, method, url, body=None, headers=None):
-        log.debug('Sending %s request to %r', method, url)
-        req = SaneHTTPRequest(method, url, body, headers or {})
-        try:
-            return self.opener.open(req)
-        except urllib2.HTTPError, e:
-            if e.code >= 300:
-                log.warning('Server returned error %d: %s', e.code, e.msg)
-                raise
-            return e
-
-    def run(self):
-        if self.local:
-            fileobj = open(self.urls[0])
-            try:
-                self._execute_build(None, fileobj)
-            finally:
-                fileobj.close()
-            return EX_OK
-
-        urls = []
-        while True:
-            if not urls:
-                urls[:] = self.urls
-            url = urls.pop(0)
-            try:
-                try:
-                    job_done = self._create_build(url)
-                    if job_done:
-                        continue
-                except urllib2.HTTPError, e:
-                    # HTTPError doesn't have the "reason" attribute of URLError
-                    log.error(e)
-                    raise ExitSlave(EX_UNAVAILABLE)
-                except urllib2.URLError, e:
-                    # Is this a temporary network glitch or something a bit
-                    # more severe?
-                    if isinstance(e.reason, socket.error) and \
-                        e.reason.args[0] in temp_net_errors:
-                        log.warning(e)
-                    else:
-                        log.error(e)
-                        raise ExitSlave(EX_UNAVAILABLE)
-            except ExitSlave, e:
-                return e.exit_code
-            time.sleep(self.poll_interval)
-
-    def quit(self):
-        log.info('Shutting down')
-        raise ExitSlave(EX_OK)
-
-    def _create_build(self, url):
-        xml = xmlio.Element('slave', name=self.name)[
-            xmlio.Element('platform', processor=self.config['processor'])[
-                self.config['machine']
-            ],
-            xmlio.Element('os', family=self.config['family'],
-                                version=self.config['version'])[
-                self.config['os']
-            ],
-        ]
-
-        log.debug('Configured packages: %s', self.config.packages)
-        for package, properties in self.config.packages.items():
-            xml.append(xmlio.Element('package', name=package, **properties))
-
-        body = str(xml)
-        log.debug('Sending slave configuration: %s', body)
-        resp = self.request('POST', url, body, {
-            'Content-Length': len(body),
-            'Content-Type': 'application/x-bitten+xml'
-        })
-
-        if resp.code == 201:
-            self._initiate_build(resp.info().get('location'))
-            return True
-        elif resp.code == 204:
-            log.info('No pending builds')
-            return False
-        else:
-            log.error('Unexpected response (%d %s)', resp.code, resp.msg)
-            raise ExitSlave(EX_PROTOCOL)
-
-    def _initiate_build(self, build_url):
-        log.info('Build pending at %s', build_url)
-        try:
-            resp = self.request('GET', build_url)
-            if resp.code == 200:
-                self._execute_build(build_url, resp)
-            else:
-                log.error('Unexpected response (%d): %s', resp.code, resp.msg)
-                self._cancel_build(build_url, exit_code=EX_PROTOCOL)
-        except KeyboardInterrupt:
-            log.warning('Build interrupted')
-            self._cancel_build(build_url)
-
-    def _execute_build(self, build_url, fileobj):
-        build_id = build_url and int(build_url.split('/')[-1]) or 0
-        xml = xmlio.parse(fileobj)
-        try:
-            recipe = Recipe(xml, os.path.join(self.work_dir, self.build_dir), 
-                            self.config)
-            basedir = recipe.ctxt.basedir
-            log.debug('Running build in directory %s' % basedir)
-            if not os.path.exists(basedir):
-                os.mkdir(basedir)
-
-            for step in recipe:
-                log.info('Executing build step %r', step.id)
-                if not self._execute_step(build_url, recipe, step):
-                    log.warning('Stopping build due to failure')
-                    break
-            else:
-                log.info('Build completed')
-            if self.dry_run:
-                self._cancel_build(build_url)
-        finally:
-            if not self.keep_files:
-                log.debug('Removing build directory %s' % basedir)
-                _rmtree(basedir)
-            if self.single_build:
-                log.info('Exiting after single build completed.')
-                raise ExitSlave(EX_OK)
-
-    def _execute_step(self, build_url, recipe, step):
-        failed = False
-        started = datetime.utcnow()
-        xml = xmlio.Element('result', step=step.id, time=started.isoformat())
-        try:
-            for type, category, generator, output in \
-                    step.execute(recipe.ctxt):
-                if type == Recipe.ERROR:
-                    failed = True
-                if type == Recipe.REPORT and self.dump_reports:
-                    print output
-                xml.append(xmlio.Element(type, category=category,
-                                         generator=generator)[
-                    output
-                ])
-        except KeyboardInterrupt:
-            log.warning('Build interrupted')
-            self._cancel_build(build_url)
-        except BuildError, e:
-            log.error('Build step %r failed (%s)', step.id, e)
-            failed = True
-        except Exception, e:
-            log.error('Internal error in build step %r', step.id, exc_info=True)
-            failed = True
-        xml.attr['duration'] = (datetime.utcnow() - started).seconds
-        if failed:
-            xml.attr['status'] = 'failure'
-            log.warning('Build step %r failed', step.id)
-        else:
-            xml.attr['status'] = 'success'
-            log.info('Build step %s completed successfully', step.id)
-
-        if not self.local and not self.dry_run:
-            try:
-                resp = self.request('POST', build_url + '/steps/', str(xml), {
-                    'Content-Type': 'application/x-bitten+xml'
-                })
-                if resp.code != 201:
-                    log.error('Unexpected response (%d): %s', resp.code,
-                              resp.msg)
-            except KeyboardInterrupt:
-                log.warning('Build interrupted')
-                self._cancel_build(build_url)
-
-        return not failed or step.onerror != 'fail'
-
-    def _cancel_build(self, build_url, exit_code=EX_OK):
-        log.info('Cancelling build at %s', build_url)
-        if not self.local:
-            resp = self.request('DELETE', build_url)
-            if resp.code not in (200, 204):
-                log.error('Unexpected response (%d): %s', resp.code, resp.msg)
-        raise ExitSlave(exit_code)
-
-
-class ExitSlave(Exception):
-    """Exception used internally by the slave to signal that the slave process
-    should be stopped.
-    """
-    def __init__(self, exit_code):
-        self.exit_code = exit_code
-        Exception.__init__(self)
-
-
-def main():
-    """Main entry point for running the build slave."""
-    from bitten import __version__ as VERSION
-    from optparse import OptionParser
-
-    parser = OptionParser(usage='usage: %prog [options] url1 [url2] ...',
-                          version='%%prog %s' % VERSION)
-    parser.add_option('--name', action='store', dest='name',
-                      help='name of this slave (defaults to host name)')
-    parser.add_option('-f', '--config', action='store', dest='config',
-                      metavar='FILE', help='path to configuration file')
-    parser.add_option('-u', '--user', dest='username',
-                      help='the username to use for authentication')
-    parser.add_option('-p', '--password', dest='password',
-                      help='the password to use when authenticating')
-
-    group = parser.add_option_group('building')
-    group.add_option('-d', '--work-dir', action='store', dest='work_dir',
-                     metavar='DIR', help='working directory for builds')
-    group.add_option('--build-dir', action='store', dest='build_dir',
-                     default = 'build_${config}_${build}',
-                     help='name pattern for the build dir to use inside the '
-                          'working dir ["%default"]')
-    group.add_option('-k', '--keep-files', action='store_true',
-                     dest='keep_files', 
-                     help='don\'t delete files after builds')
-    group.add_option('-s', '--single', action='store_true',
-                     dest='single_build',
-                     help='exit after completing a single build')
-    group.add_option('-n', '--dry-run', action='store_true', dest='dry_run',
-                     help='don\'t report results back to master')
-    group.add_option('-i', '--interval', dest='interval', metavar='SECONDS',
-                     type='int', help='time to wait between requesting builds')
-    group = parser.add_option_group('logging')
-    group.add_option('-l', '--log', dest='logfile', metavar='FILENAME',
-                     help='write log messages to FILENAME')
-    group.add_option('-v', '--verbose', action='store_const', dest='loglevel',
-                     const=logging.DEBUG, help='print as much as possible')
-    group.add_option('-q', '--quiet', action='store_const', dest='loglevel',
-                     const=logging.WARN, help='print as little as possible')
-    group.add_option('--dump-reports', action='store_true', dest='dump_reports',
-                     help='whether report data should be printed')
-
-    parser.set_defaults(dry_run=False, keep_files=False,
-                        loglevel=logging.INFO, single_build=False,
-                        dump_reports=False, interval=300)
-    options, args = parser.parse_args()
-
-    if len(args) < 1:
-        parser.error('incorrect number of arguments')
-    urls = args
-
-    logger = logging.getLogger('bitten')
-    logger.setLevel(options.loglevel)
-    handler = logging.StreamHandler()
-    handler.setLevel(options.loglevel)
-    formatter = logging.Formatter('[%(levelname)-8s] %(message)s')
-    handler.setFormatter(formatter)
-    logger.addHandler(handler)
-    if options.logfile:
-        handler = logging.FileHandler(options.logfile)
-        handler.setLevel(options.loglevel)
-        formatter = logging.Formatter('%(asctime)s [%(name)s] %(levelname)s: '
-                                      '%(message)s')
-        handler.setFormatter(formatter)
-        logger.addHandler(handler)
-
-    slave = BuildSlave(urls, name=options.name, config=options.config,
-                       dry_run=options.dry_run, work_dir=options.work_dir,
-                       build_dir=options.build_dir,
-                       keep_files=options.keep_files,
-                       single_build=options.single_build,
-                       poll_interval=options.interval,
-                       username=options.username, password=options.password,
-                       dump_reports=options.dump_reports)
-    try:
-        try:
-            exit_code = slave.run()
-        except KeyboardInterrupt:
-            slave.quit()
-    except ExitSlave, e:
-        exit_code = e.exit_code
-
-    if not options.work_dir:
-        log.debug('Removing temporary directory %s' % slave.work_dir)
-        _rmtree(slave.work_dir)
-    return exit_code
-
-if __name__ == '__main__':
-    sys.exit(main())
deleted file mode 100644
--- a/trac-0.11/bitten/templates/bitten_admin_configs.html
+++ /dev/null
@@ -1,240 +0,0 @@
-<!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:xi="http://www.w3.org/2001/XInclude"
-      xmlns:py="http://genshi.edgewall.org/">
-  <xi:include href="admin.html" />
-  <head>
-    <title>Manage Build Configurations</title>
-  </head>
-  <body>
-    <h2>Manage Build Configurations</h2>
-    <py:choose><form py:when="config" class="mod"
-         id="modconfig" method="post" action="">
-      <table class="form" summary=""><tr>
-        <td class="name"><label>Name:<br />
-          <input type="text" name="name"
-                 value="$config.name" />
-        </label></td>
-        <td class="label"><label>Label (for display):<br />
-          <input type="text" name="label" size="32"
-                 value="$config.label" />
-        </label></td>
-      </tr><tr>
-        <td colspan="2"><fieldset class="iefix">
-          <label for="description">
-            Description (you may use 
-            <a tabindex="42"
-               href="${href.wiki('WikiFormatting')}">
-              WikiFormatting</a> here):
-          </label>
-          <p>
-            <textarea id="description" name="description"
-                      class="wikitext" rows="3" cols="65">$config.description</textarea>
-          </p>
-          <script type="text/javascript" src="${chrome.htdocs_location}js/wikitoolbar.js"></script>
-        </fieldset></td>
-      </tr><tr>
-        <td colspan="2"><fieldset class="iefix">
-          <label for="recipe">Recipe:</label>
-          <p>
-            <textarea id="recipe" name="recipe" rows="8" cols="78">$config.recipe</textarea>
-          </p>
-        </fieldset></td>
-      </tr></table>
-      <fieldset id="repos">
-        <legend>Repository Mapping</legend>
-        <table class="form" summary=""><tr>
-          <th><label for="path">Path:</label></th>
-          <td colspan="3"><input id="path" type="text" name="path"
-              size="48" value="$config.path" /></td>
-        </tr><tr>
-          <th><label for="min_rev">
-            Oldest revision:
-          </label></th>
-          <td><input id="min_rev" type="text" name="min_rev" size="8"
-                     value="$config.min_rev" /></td>
-          <th><label for="max_rev">
-            Youngest revision:
-          </label></th>
-          <td><input id="max_rev" type="text" name="max_rev" size="8"
-                     value="$config.max_rev" /></td>
-        </tr></table>
-      </fieldset>
-      <div class="buttons">
-        <input type="submit" name="cancel"
-               value="Cancel" />
-        <input type="submit" name="save" value="Save" />
-      </div>
-      <div class="platforms">
-        <h3>Target Platforms</h3>
-        <table class="listing" id="platformlist">
-          <thead>
-            <tr><th class="sel">&nbsp;</th>
-            <th>Name</th><th>Rules</th></tr>
-          </thead>
-          <tbody>
-            <tr py:if="not config.platforms">
-              <td colspan="3"><em>(No Platforms)</em></td>
-            </tr>
-            <tr py:for="platform in config.platforms">
-              <td class="sel">
-                <input type="checkbox" name="sel"
-                       value="$platform.id" />
-              </td>
-              <td class="name"><a href="$platform.href">
-                $platform.name
-              </a></td>
-              <td class="rules">
-                <ul py:if="len(platform.rules)">
-                  <li py:for="rule in platform.rules">
-                    <code>
-                      <strong>$rule.property</strong> ~= 
-                      $rule.pattern
-                    </code>
-                  </li>
-                </ul>
-              </td>
-            </tr>
-          </tbody>
-        </table>
-        <div class="buttons">
-          <input type="submit" name="new"
-                 value="Add platform" />
-          <input type="submit" name="remove"
-                 value="Delete selected platforms" />
-        </div>
-      </div>
-    </form>
-
-    <form py:when="platform" class="mod" id="modplatform"
-          method="post" action="">
-      <div class="field"><label>Target Platform:
-        <input type="text" name="name"
-               value="$platform.name" />
-      </label></div>
-      <fieldset>
-        <legend>Rules</legend>
-        <table><thead><tr>
-          <th>Property name</th><th>Match pattern</th>
-        </tr></thead><tbody>
-          <tr py:for="idx, rule in enumerate(platform.rules)">
-            <td><input type="text" name="property_${idx}"
-                       value="$rule.property" /></td>
-            <td><input type="text" name="pattern_${idx}" 
-                       value="$rule.pattern" /></td>
-            <td><input type="submit"
-                       name="add_rule_${idx}" value="+" />
-                <input type="submit" name="rm_rule_${idx}"
-                       value="-" />
-            </td>
-          </tr>
-        </tbody></table>
-      </fieldset>
-      <p class="help">
-        The property name can be any of a set of standard
-        default properties, or custom properties defined
-        in slave configuration files. The default 
-        properties are:
-      </p>
-      <dl class="help">
-        <dt><code>os</code>:</dt>
-        <dd>The name of the operating system (for example
-            "Darwin")</dd>
-        <dt><code>family</code>:</dt>
-        <dd>The type of operating system (for example
-            "posix" or "nt")</dd>
-        <dt><code>version</code>:</dt>
-        <dd>The operating system version (for example
-            "8.10.1)</dd>
-        <dt><code>machine</code>:</dt>
-        <dd>The hardware architecture (for example "i386"</dd>
-        <dt><code>processor</code>:</dt>
-        <dd>The CPU model (for example "i386", this may be
-            empty or the same as for <code>machine</code>
-        </dd>
-        <dt><code>name</code>:</dt>
-        <dd>The name of the slave</dd>
-        <dt><code>ipnr</code>:</dt>
-        <dd>The IP address of the slave</dd>
-      </dl>
-      <p class="help">
-        The match pattern is a regular expression.
-      </p>
-      <div class="buttons">
-        <form method="get" action=""><div>
-          <input type="hidden"
-                 name="${platform.exists and 'edit'
-                         or 'new'}" value="" />
-          <input type="hidden" name="platform"
-                 value="$platform.id" />
-          <input type="submit" name="cancel"
-                 value="Cancel" /><py:choose>
-          <input py:when="platform.exists" type="submit"
-                 name="save" value="Save" />
-          <input py:otherwise="" type="submit"
-                 name="add" value="Add" /></py:choose>
-        </div></form>
-      </div>
-    </form>
-
-    <py:otherwise><form class="addnew" id="addcomp"
-                        method="post" action=""><fieldset>
-      <legend>Add Configuration:</legend>
-      <table summary=""><tr>
-        <td class="name"><div  
-            class="field"><label>Name:<br />
-          <input type="text" name="name" size="12" />
-        </label></div></td>
-        <td class="label"><div
-            class="field"><label>Label:<br />
-          <input type="text" name="label" size="22" />
-        </label></div></td>
-      </tr><tr>
-        <td class="path" colspan="2"><div class="field">
-          <label>Path:<br />
-            <input type="text" name="path" size="32" />
-          </label>
-        </div></td>
-      </tr></table>
-      <div class="buttons">
-        <input type="submit" name="add" value="Add" />
-      </div>
-    </fieldset></form>
-
-    <form method="post" action="">
-      <table class="listing" id="configlist">
-        <thead>
-          <tr><th class="sel">&nbsp;</th><th>Name</th>
-          <th>Path</th><th>Active</th></tr>
-        </thead><tbody>
-        <tr py:if="not configs">
-          <td colspan="4"><em>(No Build Configurations)</em></td>
-        </tr>
-        <tr py:for="config in configs">
-          <td class="sel">
-            <input type="checkbox" name="sel"
-                   value="$config.name" />
-          </td>
-          <td class="name">
-            <a href="$config.href">$config.label</a>
-          </td>
-          <td class="path"><code>$config.path</code></td>
-          <td class="active">
-            <input type="checkbox" name="active"
-                   value="$config.name"
-                   checked="${config.active and 'checked'
-                              or None}" />
-          </td>
-        </tr>
-      </tbody></table>
-      <div class="buttons">
-        <input type="submit" name="remove"
-               value="Remove selected items" />
-        <input type="submit" name="apply"
-               value="Apply changes" />
-      </div>
-    </form></py:otherwise></py:choose>
-  </body>
-</html>
deleted file mode 100644
--- a/trac-0.11/bitten/templates/bitten_admin_master.html
+++ /dev/null
@@ -1,73 +0,0 @@
-<!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:xi="http://www.w3.org/2001/XInclude"
-      xmlns:py="http://genshi.edgewall.org/">
-  <xi:include href="admin.html" />
-  <head>
-    <title>Manage Build Master</title>
-  </head>
-  <body>
-    <h2>Manage Build Master</h2>
-
-    <form class="mod" id="bitten" method="post" action="">
-      <fieldset id="config">
-        <legend>Configuration Options</legend>
-        <div class="field">
-          <label>
-            <input type="checkbox" id="build_all" name="build_all"
-                   checked="${master.build_all and 'checked' or None}" />
-            Build all revisions
-          </label>
-        </div>
-        <p class="hint">
-          Whether to build older revisions even when a more recent
-          revision has already been built.
-        </p>
-        <div class="field">
-          <label>
-            <input type="checkbox" id="adjust_timestamps"
-                   name="adjust_timestamps"
-                   checked="${master.adjust_timestamps and 'checked'
-                              or None}" />
-            Adjust build timestamps
-          </label>
-        </div>
-        <p class="hint">
-          Whether the timestamps of builds should be adjusted to be
-          close to the timestamps of the corresponding changesets.
-        </p>
-        <hr />
-        <div class="field">
-          <label>
-            Time to wait for stabilization:
-            <input type="text" id="stabilize_wait" name="stabilize_wait"
-                   value="$master.stabilize_wait" size="5" />
-          </label>
-        </div>
-        <p class="hint">
-          The time in seconds to wait for the repository to stabilize
-          after a check-in before initiating a build.
-        </p>
-        <hr />
-        <div class="field">
-          <label>
-            Connection timeout for build slaves:
-            <input type="text" id="slave_timeout" name="slave_timeout"
-                   value="$master.slave_timeout" size="5" />
-          </label>
-        </div>
-        <p class="hint">
-          The timeout in seconds after which a build started by a slave
-          is considered aborted, in case there has been no activity from
-          that slave in that time.
-        </p>
-      </fieldset>
-
-      <div class="buttons">
-        <input type="submit" value="Apply changes"/>
-      </div>
-    </form>
-  </body>
-</html>
deleted file mode 100644
--- a/trac-0.11/bitten/templates/bitten_build.html
+++ /dev/null
@@ -1,73 +0,0 @@
-<!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:xi="http://www.w3.org/2001/XInclude"
-      xmlns:py="http://genshi.edgewall.org/">
-  <xi:include href="layout.html" />
-  <head>
-    <title>$title</title>
-  </head>
-  <body>
-    <div id="content" class="build">
-      <h1>$title</h1>
-      <dl id="overview" py:with="slave = build.slave">
-        <dt class="config">Configuration:</dt>
-        <dd class="config">
-          <a href="$build.config.href">$build.config.name</a>
-        </dd>
-        <dt class="trigger">Triggered by:</dt>
-        <dd class="trigger">
-          Changeset <a href="$build.chgset_href">[$build.rev]</a> by 
-          $build.chgset_author
-        </dd>
-        <dt class="slave">Built by:</dt>
-        <dd class="slave">
-          <code>$slave.name</code> ($slave.ipnr)
-        </dd>
-        <dt class="os">Operating system:</dt>
-        <dd>$slave.os_name $slave.os_version ($slave.os_family)</dd>
-        <py:if test="slave.machine"><dt class="machine">Hardware:</dt>
-        <dd class="machine">
-          $slave.machine
-          <py:if test="slave.processor"> ($slave.processor)</py:if>
-        </dd></py:if>
-        <dt class="time">
-          ${build.stopped and 'Started:' or 'Building since:'}
-        </dt>
-        <dd class="time">$build.started ($build.started_delta ago)</dd>
-        <py:if test="build.stopped"><dt class="time">Stopped:</dt>
-        <dd class="time">$build.stopped ($build.stopped_delta ago)</dd></py:if>
-        <dt class="duration">Duration:</dt>
-        <dd class="duration">$build.duration</dd>
-      </dl>
-      <div py:if="build.can_delete" class="buttons">
-        <form method="post" action=""><div>
-          <input type="hidden" name="action" value="invalidate" />
-          <input type="submit" value="Invalidate build" />
-        </div></form>
-      </div><py:for each="step in build.steps">
-      <h2 class="step" id="step_${step.name}">$step.name ($step.duration)</h2>
-      <div py:if="step.errors" class="errors">
-        <h3>Errors</h3>
-        <ul>
-          <li py:for="error in step.errors">$error</li>
-        </ul>
-      </div>
-      <p>$step.description</p>
-      <div id="${step.name}_tabs">
-        <div class="tab">
-          <h3>Log</h3>
-          <div class="log"><py:for each="item in step.log"><code class="$item.level">$item.message</code><br /></py:for></div>
-        </div>
-        <div py:for="report in [r for r in step.reports if r.template]"
-             class="tab report $report.category">
-          <xi:include href="$report.template" py:with="data = report.data" />
-        </div>
-      </div>
-      <script type="text/javascript">
-        makeTabSet(document.getElementById("${step.name}_tabs"));
-      </script></py:for>
-    </div>
-  </body>
-</html>
deleted file mode 100644
--- a/trac-0.11/bitten/templates/bitten_chart_coverage.html
+++ /dev/null
@@ -1,38 +0,0 @@
-<chart xmlns:py="http://genshi.edgewall.org/">
- <chart_type>
-  <value>area</value>
-  <value>area</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>
-  <color>bbbbbb</color>
-  <color>9999ff</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>
deleted file mode 100644
--- a/trac-0.11/bitten/templates/bitten_chart_tests.html
+++ /dev/null
@@ -1,38 +0,0 @@
-<chart xmlns:py="http://genshi.edgewall.org/">
- <chart_type>
-  <value>area</value>
-  <value>column</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="2"
-   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>
-  <color>99dd99</color>
-  <color>ff0000</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>
deleted file mode 100644
--- a/trac-0.11/bitten/templates/bitten_config.html
+++ /dev/null
@@ -1,178 +0,0 @@
-<!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:xi="http://www.w3.org/2001/XInclude"
-      xmlns:py="http://genshi.edgewall.org/">
-  <xi:include href="layout.html" />
-  <head>
-    <title>$title</title>
-  </head>
-  <body>
-    <strong py:def="build_status(status)" class="status">
-      <py:choose test="status">
-      <py:when test="'completed'">Success</py:when>
-      <py:when test="'failed'">Failed</py:when>
-      <py:when test="'in progress'">In-progress</py:when>
-      </py:choose>
-    </strong>
-    
-    <div py:def="slave_info(slave)" class="system">
-      <strong>$slave.name</strong> ($slave.ipnr)<br />
-      $slave.os_name $slave.os_version
-      <py:if test="slave.machine or slave.processor"> / </py:if>
-      ${slave.processor or slave.machine or ''}
-    </div>
-    
-    <ul py:def="build_steps(steps)" py:if="steps" class="steps">
-      <li py:for="step in steps"
-          class="${step.failed and 'failed' or 'success'}">
-        <span class="duration">$step.duration</span> 
-        <a href="$step.href" title="${step.description or None}">
-          $step.name
-        </a>
-        <ul py:if="step.failed and step.errors">
-          <li py:for="error in step.errors">$error</li>
-        </ul>
-      </li>
-    </ul>
- 
-    <div id="content" class="build">
-      <h1>$title</h1><py:choose test="page_mode"><py:when test="'overview'">
-      <form id="prefs" method="get" action="">
-        <div>
-          <input type="checkbox" id="showall" name="show" value="all"
-                 checked="${show_all and 'checked' or None}" />
-          <label for="showall">Show deactivated configurations</label>
-        </div>
-        <div class="buttons">
-          <input type="submit" value="Update" />
-        </div>
-      </form><py:for each="config in configs">
-      <h2 class="config ${not config.active and 'deactivated' or ''}">
-        <a href="$config.href">$config.label</a>
-      </h2>
-      <div py:if="config.description" class="description">
-        $config.description
-      </div><py:if test="len(config.builds)">
-      <h3 class="builds"><a href="$config.href">Latest builds</a></h3>
-      <table class="builds"><tbody><tr>
-        <th py:with="youngest_rev = config.youngest_rev">
-          <a href="$youngest_rev.href">[$youngest_rev.id]</a>
-          by $youngest_rev.author<p class="date">$youngest_rev.date</p>
-          <p class="message">$youngest_rev.message</p>
-        </th>
-        <td py:for="build in config.builds" class="$build.cls"><py:choose>
-        <py:when test="build.status != 'pending'">
-          <a href="$build.href">$build.platform</a>
-          <p class="date">$build.stopped</p>
-          ${slave_info(build.slave)}
-          ${build_status(build.status)}
-        </py:when><py:otherwise>
-          <strong>$build.platform</strong>
-          <p class="nobuild">No build yet</p>
-        </py:otherwise></py:choose>
-        </td>
-      </tr></tbody></table></py:if></py:for>
-
-    </py:when><py:when test="'view_config'">
-      <form py:if="config.can_modify" id="prefs" method="post"
-            class="activation"><py:choose>
-        <div py:when="not config.active" class="help">
-          This build configuration is currently inactive.<br />
-          No builds will be initiated for this configuration<br />
-          until it is activated.
-        </div>
-        <div py:otherwise="" class="help">
-          This configuration is currently active.
-        </div></py:choose>
-        <div class="buttons" py:choose="">
-          <input type="hidden" name="action" value="edit" />
-          <input py:when="config.active" type="submit" name="deactivate"
-                 value="Deactivate" />
-          <input py:otherwise="" type="submit" name="activate"
-                 value="Activate" />
-        </div>
-      </form>
-      <p class="path">
-        Repository path: 
-        <a py:if="config.path" href="$config.browser_href">$config.path</a>
-        ${not config.path and '&mdash;' or ''}
-        <py:if test="config.min_rev or config.max_rev">
-        (<py:if test="config.min_rev">starting at 
-         <a href="$config.min_rev_href">[$config.min_rev]</a></py:if>
-         <py:if test="config.min_rev and config.max_rev">, </py:if>
-         <py:if test="config.max_rev">up to 
-         <a href="$config.max_rev_href">[$config.max_rev]</a></py:if>)
-        </py:if>
-      </p>
-      <div py:if="config.description" class="description">
-        $config.description
-      </div>
-      <div id="charts"><py:for each="chart in config.charts">
-        <object type="application/x-shockwave-flash"
-                width="320" height="240"
-                data="${href.chrome('bitten', 'charts.swf')}">
-          <param name="movie" value="${href.chrome('bitten', 'charts.swf')}" />
-          <param name="FlashVars"
-                 value="library_path=${href.chrome('bitten')}&amp;xml_source=$chart.href${config.charts_license and '&amp;license='+config.charts_license or ''}" />
-          <param name="wmode" value="transparent" />
-        </object><br /></py:for>
-      </div>
-
-      <table py:if="config.platforms and config.builds"
-             class="listing" id="builds">
-        <thead><tr>
-          <th class="chgset"><abbr title="Changeset">Chgset</abbr></th>
-          <th py:for="platform in config.platforms">$platform.name</th>
-        </tr></thead>
-        <tbody py:if="config.builds">
-          <tr py:for="rev_num in sorted(config.builds, reverse=True)"
-              py:with="rev = config.builds[rev_num]">
-            <th class="chgset" scope="row">
-              <a href="$rev.href" title="View Changeset">[$rev_num]</a>
-            </th><py:for each="platform in config.platforms"><py:choose>
-            <td py:when="platform.id in rev" py:with="build = rev[platform.id]"
-                class="$build.cls">
-              <div class="info">
-                <a href="$build.href" title="View build results">
-                  $build.id
-                  ${build_status(build.status)}
-                </a>
-                ${slave_info(build.slave)}
-              </div>
-              ${build_steps(build.steps)}
-            </td>
-            <td py:otherwise="">&mdash;</td></py:choose></py:for>
-          </tr>
-        </tbody>
-      </table>
-      <br style="clear: right"/>
-
-    </py:when><py:when test="'view-inprogress'">
-      <py:for each="config in configs">
-      <h2 class="config ${not config.active and 'deactivated' or ''}">
-        <a href="$config.href">$config.label</a>
-      </h2>
-      <table class="listing" id="builds">
-        <thead><tr>
-          <th class="chgset" abbrev="Changeset">Chgset</th><th>Build</th>
-        </tr></thead><tbody>
-        <tr py:for="build in config.builds">
-          <th class="chgset" scope="row">
-            <a href="$build.rev_href" title="View Changeset">[$build.rev]</a>
-          </th>
-          <td class="$build.cls">
-            <div class="info">
-              <a href="$build.href" title="View build results">
-                $build.id: <strong class="status">$build.platform</strong>
-              </a>
-              ${slave_info(build.slave)}
-            </div>
-            ${build_steps(build.steps)}
-          </td>
-        </tr></tbody>
-      </table></py:for></py:when>
-    </py:choose></div>
-  </body>
-</html>
deleted file mode 100644
--- a/trac-0.11/bitten/templates/bitten_summary_coverage.html
+++ /dev/null
@@ -1,27 +0,0 @@
-<!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/" py:strip="">
- <body py:strip="">
-  <h3>Code Coverage</h3>
-  <table class="listing coverage">
-   <thead><tr>
-    <th class="name">Unit</th><th class="loc">Lines of Code</th>
-    <th class="cov">Coverage</th>
-   </tr></thead>
-   <tbody><tr py:for="item in data.units">
-    <td class="name" py:choose="">
-     <a py:when="item.href" href="$item.href">$item.name</a>
-     <py:otherwise>$item.name</py:otherwise>
-    </td>
-    <td class="loc">$item.loc</td>
-    <td class="cov">${item.cov}%</td>
-   </tr></tbody>
-   <tbody class="totals"><tr py:with="totals = data.totals">
-    <th>Total</th><td>$totals.loc</td>
-    <td>${totals.cov}%</td>
-   </tr></tbody>
-  </table>
- </body>
-</html>
deleted file mode 100644
--- a/trac-0.11/bitten/templates/bitten_summary_tests.html
+++ /dev/null
@@ -1,31 +0,0 @@
-<!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/" py:strip="">
- <body py:strip="">
-  <h3>Test Results</h3>
-  <table class="listing tests">
-   <thead><tr>
-    <th>Test Fixture</th><th>Total</th>
-    <th>Failures</th><th>Errors</th>
-   </tr></thead>
-   <tbody><tr py:for="item in data.fixtures"
-              class="${item.num_failure or item.num_error and 'failed' or None}">
-    <th py:choose="">
-     <a py:when="item.href" href="$item.href">$item.name</a>
-     <py:otherwise>$item.name</py:otherwise>
-    </th>
-    <td>${item.num_success + item.num_failure + item.num_error}</td>
-    <td>$item.num_failure</td>
-    <td>$item.num_error</td>
-   </tr></tbody>
-   <tbody class="totals"><tr py:with="totals = data.totals">
-    <th>Total</th>
-    <td>$totals.success</td>
-    <td>$totals.failure</td>
-    <td>$totals.error</td>
-   </tr></tbody>
-  </table>
- </body>
-</html>
deleted file mode 100644
--- a/trac-0.11/bitten/tests/__init__.py
+++ /dev/null
@@ -1,33 +0,0 @@
-# -*- 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 bitten.tests import admin, master, model, recipe, queue, slave, web_ui
-from bitten.build import tests as build
-from bitten.report import tests as report
-from bitten.util import tests as util
-
-def suite():
-    suite = unittest.TestSuite()
-    suite.addTest(admin.suite())
-    suite.addTest(master.suite())
-    suite.addTest(model.suite())
-    suite.addTest(recipe.suite())
-    suite.addTest(queue.suite())
-    suite.addTest(slave.suite())
-    suite.addTest(web_ui.suite())
-    suite.addTest(build.suite())
-    suite.addTest(report.suite())
-    suite.addTest(util.suite())
-    return suite
-
-if __name__ == '__main__':
-    unittest.main(defaultTest='suite')
deleted file mode 100644
--- a/trac-0.11/bitten/tests/admin.py
+++ /dev/null
@@ -1,810 +0,0 @@
-# -*- coding: utf-8 -*-
-#
-# 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 shutil
-import tempfile
-import unittest
-
-from trac.core import TracError
-from trac.db import DatabaseManager
-from trac.perm import PermissionCache, PermissionError, PermissionSystem
-from trac.test import EnvironmentStub, Mock
-from trac.web.href import Href
-from trac.web.main import RequestDone
-from bitten.main import BuildSystem
-from bitten.model import BuildConfig, TargetPlatform, schema
-from bitten.admin import BuildMasterAdminPageProvider, \
-                         BuildConfigurationsAdminPageProvider
-
-try:
-    from trac.perm import DefaultPermissionPolicy
-except ImportError:
-    DefaultPermissionPolicy = None
-
-class BuildMasterAdminPageProviderTestCase(unittest.TestCase):
-
-    def setUp(self):
-        self.env = EnvironmentStub(enable=['trac.*', 'bitten.*'])
-        self.env.path = tempfile.mkdtemp()
-
-        # Create tables
-        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)
-
-        # Set up permissions
-        self.env.config.set('trac', 'permission_store',
-                            'DefaultPermissionStore')
-        PermissionSystem(self.env).grant_permission('joe', 'BUILD_ADMIN')
-        if DefaultPermissionPolicy is not None and hasattr(DefaultPermissionPolicy, "CACHE_EXPIRY"):
-            self.old_perm_cache_expiry = DefaultPermissionPolicy.CACHE_EXPIRY
-            DefaultPermissionPolicy.CACHE_EXPIRY = -1
-
-        # Hook up a dummy repository
-        self.repos = Mock(
-            get_node=lambda path, rev=None: Mock(get_history=lambda: [],
-                                                 isdir=True),
-            normalize_path=lambda path: path,
-            sync=lambda: None
-        )
-        self.env.get_repository = lambda authname=None: self.repos
-
-    def tearDown(self):
-        if DefaultPermissionPolicy is not None and hasattr(DefaultPermissionPolicy, "CACHE_EXPIRY"):
-            DefaultPermissionPolicy.CACHE_EXPIRY = self.old_perm_cache_expiry
-        shutil.rmtree(self.env.path)
-
-    def test_get_admin_panels(self):
-        provider = BuildMasterAdminPageProvider(self.env)
-
-        req = Mock(perm=PermissionCache(self.env, 'joe'))
-        self.assertEqual([('bitten', 'Builds', 'master', 'Master Settings')],
-                         list(provider.get_admin_panels(req)))
-
-        PermissionSystem(self.env).revoke_permission('joe', 'BUILD_ADMIN')
-        req = Mock(perm=PermissionCache(self.env, 'joe'))
-        self.assertEqual([], list(provider.get_admin_panels(req)))
-
-    def test_process_get_request(self):
-        req = Mock(method='GET', chrome={}, href=Href('/'),
-                   perm=PermissionCache(self.env, 'joe'))
-
-        provider = BuildMasterAdminPageProvider(self.env)
-        template_name, data = provider.render_admin_panel(
-            req, 'bitten', 'master', ''
-        )
-
-        self.assertEqual('bitten_admin_master.html', template_name)
-        assert 'master' in data
-        master = data['master']
-        self.assertEqual(3600, master.slave_timeout)
-        self.assertEqual(0, master.stabilize_wait)
-        assert not master.adjust_timestamps
-        assert not master.build_all
-
-    def test_process_config_changes(self):
-        redirected_to = []
-        def redirect(url):
-            redirected_to.append(url)
-            raise RequestDone
-        req = Mock(method='POST', perm=PermissionCache(self.env, 'joe'),
-                   abs_href=Href('http://example.org/'), redirect=redirect,
-                   args={'slave_timeout': '60', 'adjust_timestamps': ''})
-
-        provider = BuildMasterAdminPageProvider(self.env)
-        try:
-            provider.render_admin_panel(req, 'bitten', 'master', '')
-            self.fail('Expected RequestDone')
-
-        except RequestDone:
-            self.assertEqual('http://example.org/admin/bitten/master',
-                             redirected_to[0])
-            section = self.env.config['bitten']
-            self.assertEqual(60, section.getint('slave_timeout'))
-            self.assertEqual(True, section.getbool('adjust_timestamps'))
-            self.assertEqual(False, section.getbool('build_all'))
-
-
-class BuildConfigurationsAdminPageProviderTestCase(unittest.TestCase):
-
-    def setUp(self):
-        self.env = EnvironmentStub(enable=['trac.*', 'bitten.*'])
-        self.env.path = tempfile.mkdtemp()
-
-        # Create tables
-        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)
-
-        # Set up permissions
-        self.env.config.set('trac', 'permission_store',
-                            'DefaultPermissionStore')
-        PermissionSystem(self.env).grant_permission('joe', 'BUILD_CREATE')
-        PermissionSystem(self.env).grant_permission('joe', 'BUILD_DELETE')
-        PermissionSystem(self.env).grant_permission('joe', 'BUILD_MODIFY')
-        if DefaultPermissionPolicy is not None and hasattr(DefaultPermissionPolicy, "CACHE_EXPIRY"):
-            self.old_perm_cache_expiry = DefaultPermissionPolicy.CACHE_EXPIRY
-            DefaultPermissionPolicy.CACHE_EXPIRY = -1
-
-        # Hook up a dummy repository
-        self.repos = Mock(
-            get_node=lambda path, rev=None: Mock(get_history=lambda: [],
-                                                 isdir=True),
-            normalize_path=lambda path: path,
-            sync=lambda: None
-        )
-        self.env.get_repository = lambda authname=None: self.repos
-
-    def tearDown(self):
-        if DefaultPermissionPolicy is not None and hasattr(DefaultPermissionPolicy, "CACHE_EXPIRY"):
-            DefaultPermissionPolicy.CACHE_EXPIRY = self.old_perm_cache_expiry
-        shutil.rmtree(self.env.path)
-
-    def test_get_admin_panels(self):
-        provider = BuildConfigurationsAdminPageProvider(self.env)
-
-        req = Mock(perm=PermissionCache(self.env, 'joe'))
-        self.assertEqual([('bitten', 'Builds', 'configs', 'Configurations')],
-                         list(provider.get_admin_panels(req)))
-
-        PermissionSystem(self.env).revoke_permission('joe', 'BUILD_MODIFY')
-        req = Mock(perm=PermissionCache(self.env, 'joe'))
-        self.assertEqual([], list(provider.get_admin_panels(req)))
-
-    def test_process_view_configs_empty(self):
-        req = Mock(method='GET', chrome={}, href=Href('/'),
-                   perm=PermissionCache(self.env, 'joe'))
-
-        provider = BuildConfigurationsAdminPageProvider(self.env)
-        template_name, data = provider.render_admin_panel(
-            req, 'bitten', 'configs', ''
-        )
-
-        self.assertEqual('bitten_admin_configs.html', template_name)
-        self.assertEqual([], data['configs'])
-
-    def test_process_view_configs(self):
-        BuildConfig(self.env, name='foo', label='Foo', path='branches/foo',
-                    active=True).insert()
-        BuildConfig(self.env, name='bar', label='Bar', path='branches/bar',
-                    min_rev='123', max_rev='456').insert()
-
-        req = Mock(method='GET', chrome={}, href=Href('/'),
-                   perm=PermissionCache(self.env, 'joe'))
-
-        provider = BuildConfigurationsAdminPageProvider(self.env)
-        template_name, data = provider.render_admin_panel(
-            req, 'bitten', 'configs', ''
-        )
-
-        self.assertEqual('bitten_admin_configs.html', template_name)
-        assert 'configs' in data
-        configs = data['configs']
-        self.assertEqual(2, len(configs))
-        self.assertEqual({
-            'name': 'bar', 'href': '/admin/bitten/configs/bar',
-            'label': 'Bar', 'min_rev': '123', 'max_rev': '456',
-            'path': 'branches/bar', 'active': False
-        }, configs[0])
-        self.assertEqual({
-            'name': 'foo', 'href': '/admin/bitten/configs/foo',
-            'label': 'Foo', 'min_rev': None, 'max_rev': None,
-            'path': 'branches/foo', 'active': True
-        }, configs[1])
-
-    def test_process_view_config(self):
-        BuildConfig(self.env, name='foo', label='Foo', path='branches/foo',
-                    active=True).insert()
-        TargetPlatform(self.env, config='foo', name='any').insert()
-
-        req = Mock(method='GET', chrome={}, href=Href('/'),
-                   perm=PermissionCache(self.env, 'joe'))
-
-        provider = BuildConfigurationsAdminPageProvider(self.env)
-        template_name, data = provider.render_admin_panel(
-            req, 'bitten', 'configs', 'foo'
-        )
-
-        self.assertEqual('bitten_admin_configs.html', template_name)
-        assert 'config' in data
-        config = data['config']
-        self.assertEqual({
-            'name': 'foo', 'label': 'Foo', 'description': '', 'recipe': '',
-            'path': 'branches/foo', 'min_rev': None, 'max_rev': None,
-            'active': True, 'platforms': [{
-                'href': '/admin/bitten/configs/foo/1',
-                'name': 'any', 'id': 1, 'rules': []
-            }]
-        }, config)
-
-    def test_process_activate_config(self):
-        BuildConfig(self.env, name='foo', path='branches/foo').insert()
-        BuildConfig(self.env, name='bar', path='branches/bar').insert()
-
-        redirected_to = []
-        def redirect(url):
-            redirected_to.append(url)
-            raise RequestDone
-        req = Mock(method='POST', perm=PermissionCache(self.env, 'joe'),
-                   abs_href=Href('http://example.org/'), redirect=redirect,
-                   authname='joe',
-                   args={'apply': '', 'active': ['foo']})
-
-        provider = BuildConfigurationsAdminPageProvider(self.env)
-        try:
-            provider.render_admin_panel(req, 'bitten', 'configs', '')
-            self.fail('Expected RequestDone')
-
-        except RequestDone:
-            self.assertEqual('http://example.org/admin/bitten/configs',
-                             redirected_to[0])
-            config = BuildConfig.fetch(self.env, name='foo')
-            self.assertEqual(True, config.active)
-
-    def test_process_deactivate_config(self):
-        BuildConfig(self.env, name='foo', path='branches/foo',
-                    active=True).insert()
-        BuildConfig(self.env, name='bar', path='branches/bar',
-                    active=True).insert()
-
-        redirected_to = []
-        def redirect(url):
-            redirected_to.append(url)
-            raise RequestDone
-        req = Mock(method='POST', perm=PermissionCache(self.env, 'joe'),
-                   abs_href=Href('http://example.org/'), redirect=redirect,
-                   authname='joe',
-                   args={'apply': ''})
-
-        provider = BuildConfigurationsAdminPageProvider(self.env)
-        try:
-            provider.render_admin_panel(req, 'bitten', 'configs', '')
-            self.fail('Expected RequestDone')
-
-        except RequestDone:
-            self.assertEqual('http://example.org/admin/bitten/configs',
-                             redirected_to[0])
-            config = BuildConfig.fetch(self.env, name='foo')
-            self.assertEqual(False, config.active)
-            config = BuildConfig.fetch(self.env, name='bar')
-            self.assertEqual(False, config.active)
-
-    def test_process_add_config(self):
-        BuildConfig(self.env, name='foo', label='Foo', path='branches/foo',
-                    active=True).insert()
-
-        redirected_to = []
-        def redirect(url):
-            redirected_to.append(url)
-            raise RequestDone
-        req = Mock(method='POST', perm=PermissionCache(self.env, 'joe'),
-                   abs_href=Href('http://example.org/'), redirect=redirect,
-                   authname='joe',
-                   args={'add': '', 'name': 'bar', 'label': 'Bar'})
-
-        provider = BuildConfigurationsAdminPageProvider(self.env)
-        try:
-            provider.render_admin_panel(req, 'bitten', 'configs', '')
-            self.fail('Expected RequestDone')
-
-        except RequestDone:
-            self.assertEqual('http://example.org/admin/bitten/configs/bar',
-                             redirected_to[0])
-            config = BuildConfig.fetch(self.env, name='bar')
-            self.assertEqual('Bar', config.label)
-
-    def test_process_add_config_cancel(self):
-        redirected_to = []
-        def redirect(url):
-            redirected_to.append(url)
-            raise RequestDone
-        req = Mock(method='POST', perm=PermissionCache(self.env, 'joe'),
-                   abs_href=Href('http://example.org/'), redirect=redirect,
-                   args={'cancel': '', 'name': 'bar', 'label': 'Bar'})
-
-        provider = BuildConfigurationsAdminPageProvider(self.env)
-        try:
-            provider.render_admin_panel(req, 'bitten', 'configs', '')
-            self.fail('Expected RequestDone')
-
-        except RequestDone:
-            self.assertEqual('http://example.org/admin/bitten/configs',
-                             redirected_to[0])
-            configs = list(BuildConfig.select(self.env, include_inactive=True))
-            self.assertEqual(0, len(configs))
-
-    def test_process_add_config_no_name(self):
-        req = Mock(method='POST', perm=PermissionCache(self.env, 'joe'),
-                   args={'add': ''})
-
-        provider = BuildConfigurationsAdminPageProvider(self.env)
-        try:
-            provider.render_admin_panel(req, 'bitten', 'configs', '')
-            self.fail('Expected TracError')
-
-        except TracError, e:
-            self.assertEqual('Missing required field "name"', e.message)
-            self.assertEqual('Missing Field', e.title)
-
-    def test_process_add_config_invalid_name(self):
-        req = Mock(method='POST', perm=PermissionCache(self.env, 'joe'),
-                   args={'add': '', 'name': 'no spaces allowed'})
-
-        provider = BuildConfigurationsAdminPageProvider(self.env)
-        try:
-            provider.render_admin_panel(req, 'bitten', 'configs', '')
-            self.fail('Expected TracError')
-
-        except TracError, e:
-            self.assertEqual('The field "name" may only contain letters, '
-                             'digits, periods, or dashes.', e.message)
-            self.assertEqual('Invalid Field', e.title)
-
-    def test_new_config_submit_with_invalid_path(self):
-        req = Mock(method='POST', perm=PermissionCache(self.env, 'joe'),
-                   authname='joe',
-                   args={'add': '', 'name': 'foo', 'path': 'invalid/path'})
-
-        def get_node(path, rev=None):
-            raise TracError('No such node')
-        self.repos = Mock(get_node=get_node)
-
-        provider = BuildConfigurationsAdminPageProvider(self.env)
-        try:
-            provider.render_admin_panel(req, 'bitten', 'configs', '')
-            self.fail('Expected TracError')
-
-        except TracError, e:
-            self.assertEqual('No such node', e.message)
-            self.assertEqual('Invalid Repository Path', e.title)
-
-    def test_process_add_config_no_perms(self):
-        BuildConfig(self.env, name='foo', label='Foo', path='branches/foo',
-                    active=True).insert()
-        PermissionSystem(self.env).revoke_permission('joe', 'BUILD_CREATE')
-
-        req = Mock(method='POST',
-                   perm=PermissionCache(self.env, 'joe'),
-                   args={'add': '', 'name': 'bar', 'label': 'Bar'})
-
-        provider = BuildConfigurationsAdminPageProvider(self.env)
-        self.assertRaises(PermissionError, provider.render_admin_panel, req,
-                          'bitten', 'configs', '')
-
-    def test_process_remove_config(self):
-        BuildConfig(self.env, name='foo', label='Foo', path='branches/foo',
-                    active=True).insert()
-        BuildConfig(self.env, name='bar', label='Bar', path='branches/bar',
-                    min_rev='123', max_rev='456').insert()
-
-        redirected_to = []
-        def redirect(url):
-            redirected_to.append(url)
-            raise RequestDone
-        req = Mock(method='POST', perm=PermissionCache(self.env, 'joe'),
-                   abs_href=Href('http://example.org/'), redirect=redirect,
-                   args={'remove': '', 'sel': 'bar'})
-
-        provider = BuildConfigurationsAdminPageProvider(self.env)
-        try:
-            provider.render_admin_panel(req, 'bitten', 'configs', '')
-            self.fail('Expected RequestDone')
-
-        except RequestDone:
-            self.assertEqual('http://example.org/admin/bitten/configs',
-                             redirected_to[0])
-            assert not BuildConfig.fetch(self.env, name='bar')
-
-    def test_process_remove_config_cancel(self):
-        BuildConfig(self.env, name='foo', label='Foo', path='branches/foo',
-                    active=True).insert()
-        BuildConfig(self.env, name='bar', label='Bar', path='branches/bar',
-                    min_rev='123', max_rev='456').insert()
-
-        redirected_to = []
-        def redirect(url):
-            redirected_to.append(url)
-            raise RequestDone
-        req = Mock(method='POST', perm=PermissionCache(self.env, 'joe'),
-                   abs_href=Href('http://example.org/'), redirect=redirect,
-                   args={'cancel': '', 'sel': 'bar'})
-
-        provider = BuildConfigurationsAdminPageProvider(self.env)
-        try:
-            provider.render_admin_panel(req, 'bitten', 'configs', '')
-            self.fail('Expected RequestDone')
-
-        except RequestDone:
-            self.assertEqual('http://example.org/admin/bitten/configs',
-                             redirected_to[0])
-            configs = list(BuildConfig.select(self.env, include_inactive=True))
-            self.assertEqual(2, len(configs))
-
-    def test_process_remove_config_no_selection(self):
-        BuildConfig(self.env, name='foo', label='Foo', path='branches/foo',
-                    active=True).insert()
-
-        req = Mock(method='POST', perm=PermissionCache(self.env, 'joe'),
-                   args={'remove': ''})
-
-        provider = BuildConfigurationsAdminPageProvider(self.env)
-        try:
-            provider.render_admin_panel(req, 'bitten', 'configs', '')
-            self.fail('Expected TracError')
-
-        except TracError, e:
-            self.assertEqual('No configuration selected', e.message)
-
-    def test_process_remove_config_bad_selection(self):
-        BuildConfig(self.env, name='foo', label='Foo', path='branches/foo',
-                    active=True).insert()
-
-        req = Mock(method='POST', perm=PermissionCache(self.env, 'joe'),
-                   args={'remove': '', 'sel': 'baz'})
-
-        provider = BuildConfigurationsAdminPageProvider(self.env)
-        try:
-            provider.render_admin_panel(req, 'bitten', 'configs', '')
-            self.fail('Expected TracError')
-
-        except TracError, e:
-            self.assertEqual("Configuration 'baz' not found", e.message)
-
-    def test_process_remove_config_no_perms(self):
-        BuildConfig(self.env, name='foo', label='Foo', path='branches/foo',
-                    active=True).insert()
-        PermissionSystem(self.env).revoke_permission('joe', 'BUILD_DELETE')
-
-        req = Mock(method='POST',
-                   perm=PermissionCache(self.env, 'joe'),
-                   args={'remove': '', 'sel': 'bar'})
-
-        provider = BuildConfigurationsAdminPageProvider(self.env)
-        self.assertRaises(PermissionError, provider.render_admin_panel, req,
-                          'bitten', 'configs', '')
-
-    def test_process_update_config(self):
-        BuildConfig(self.env, name='foo', label='Foo', path='branches/foo',
-                    active=True).insert()
-
-        redirected_to = []
-        def redirect(url):
-            redirected_to.append(url)
-            raise RequestDone
-        req = Mock(method='POST', perm=PermissionCache(self.env, 'joe'),
-                   abs_href=Href('http://example.org/'), redirect=redirect,
-                   authname='joe', args={
-            'save': '', 'name': 'foo', 'label': 'Foobar',
-            'description': 'Thanks for all the fish!'
-        })
-
-        provider = BuildConfigurationsAdminPageProvider(self.env)
-        try:
-            provider.render_admin_panel(req, 'bitten', 'configs', 'foo')
-            self.fail('Expected RequestDone')
-
-        except RequestDone:
-            self.assertEqual('http://example.org/admin/bitten/configs',
-                             redirected_to[0])
-            config = BuildConfig.fetch(self.env, name='foo')
-            self.assertEqual('Foobar', config.label)
-            self.assertEqual('Thanks for all the fish!', config.description)
-
-    def test_process_update_config_no_name(self):
-        BuildConfig(self.env, name='foo', label='Foo', path='branches/foo',
-                    active=True).insert()
-
-        req = Mock(method='POST', perm=PermissionCache(self.env, 'joe'),
-                   args={'save': ''})
-
-        provider = BuildConfigurationsAdminPageProvider(self.env)
-        try:
-            provider.render_admin_panel(req, 'bitten', 'configs', 'foo')
-            self.fail('Expected TracError')
-
-        except TracError, e:
-            self.assertEqual('Missing required field "name"', e.message)
-            self.assertEqual('Missing Field', e.title)
-
-    def test_process_update_config_invalid_name(self):
-        BuildConfig(self.env, name='foo', label='Foo', path='branches/foo',
-                    active=True).insert()
-
-        req = Mock(method='POST', perm=PermissionCache(self.env, 'joe'),
-                   args={'save': '', 'name': 'no spaces allowed'})
-
-        provider = BuildConfigurationsAdminPageProvider(self.env)
-        try:
-            provider.render_admin_panel(req, 'bitten', 'configs', 'foo')
-            self.fail('Expected TracError')
-
-        except TracError, e:
-            self.assertEqual('The field "name" may only contain letters, '
-                             'digits, periods, or dashes.', e.message)
-            self.assertEqual('Invalid Field', e.title)
-
-    def test_process_update_config_invalid_path(self):
-        BuildConfig(self.env, name='foo', label='Foo', path='branches/foo',
-                    active=True).insert()
-
-        req = Mock(method='POST', perm=PermissionCache(self.env, 'joe'),
-                   authname='joe',
-                   args={'save': '', 'name': 'foo', 'path': 'invalid/path'})
-
-        def get_node(path, rev=None):
-            raise TracError('No such node')
-        self.repos = Mock(get_node=get_node)
-
-        provider = BuildConfigurationsAdminPageProvider(self.env)
-        try:
-            provider.render_admin_panel(req, 'bitten', 'configs', 'foo')
-            self.fail('Expected TracError')
-
-        except TracError, e:
-            self.assertEqual('No such node', e.message)
-            self.assertEqual('Invalid Repository Path', e.title)
-
-    def test_process_update_config_non_wellformed_recipe(self):
-        BuildConfig(self.env, name='foo', label='Foo', path='branches/foo',
-                    active=True).insert()
-
-        req = Mock(method='POST', perm=PermissionCache(self.env, 'joe'),
-                   authname='joe',
-                   args={'save': '', 'name': 'foo', 'recipe': 'not_xml'})
-
-        provider = BuildConfigurationsAdminPageProvider(self.env)
-        try:
-            provider.render_admin_panel(req, 'bitten', 'configs', 'foo')
-            self.fail('Expected TracError')
-
-        except TracError, e:
-            self.assertEqual('Failure parsing recipe: syntax error: line 1, '
-                             'column 0', e.message)
-            self.assertEqual('Invalid Recipe', e.title)
-
-    def test_process_update_config_invalid_recipe(self):
-        BuildConfig(self.env, name='foo', label='Foo', path='branches/foo',
-                    active=True).insert()
-
-        req = Mock(method='POST', perm=PermissionCache(self.env, 'joe'),
-                   authname='joe',
-                   args={'save': '', 'name': 'foo',
-                         'recipe': '<build><step /></build>'})
-
-        provider = BuildConfigurationsAdminPageProvider(self.env)
-        try:
-            provider.render_admin_panel(req, 'bitten', 'configs', 'foo')
-            self.fail('Expected TracError')
-
-        except TracError, e:
-            self.assertEqual('Steps must have an "id" attribute', e.message)
-            self.assertEqual('Invalid Recipe', e.title)
-
-    def test_process_new_platform(self):
-        BuildConfig(self.env, name='foo', label='Foo', path='branches/foo',
-                    active=True).insert()
-
-        data = {}
-        req = Mock(method='POST', chrome={}, hdf=data, href=Href('/'),
-                   perm=PermissionCache(self.env, 'joe'),
-                   args={'new': ''})
-
-        provider = BuildConfigurationsAdminPageProvider(self.env)
-        template_name, data = provider.render_admin_panel(
-            req, 'bitten', 'configs', 'foo'
-        )
-
-        self.assertEqual('bitten_admin_configs.html', template_name)
-        assert 'platform' in data
-        platform = data['platform']
-        self.assertEqual({
-            'id': None, 'exists': False, 'name': None, 'rules': [('', '')],
-        }, platform)
-
-    def test_process_add_platform(self):
-        BuildConfig(self.env, name='foo', label='Foo', path='branches/foo',
-                    active=True).insert()
-
-        redirected_to = []
-        def redirect(url):
-            redirected_to.append(url)
-            raise RequestDone
-        req = Mock(method='POST', perm=PermissionCache(self.env, 'joe'),
-                   abs_href=Href('http://example.org/'), redirect=redirect,
-                   authname='joe',
-                   args={'add': '', 'new': '', 'name': 'Test',
-                         'property_0': 'family', 'pattern_0': 'posix'})
-
-        provider = BuildConfigurationsAdminPageProvider(self.env)
-        try:
-            provider.render_admin_panel(req, 'bitten', 'configs', 'foo')
-            self.fail('Expected RequestDone')
-
-        except RequestDone:
-            self.assertEqual('http://example.org/admin/bitten/configs/foo',
-                             redirected_to[0])
-            platforms = list(TargetPlatform.select(self.env, config='foo'))
-            self.assertEqual(1, len(platforms))
-            self.assertEqual('Test', platforms[0].name)
-            self.assertEqual([('family', 'posix')], platforms[0].rules)
-
-    def test_process_add_platform_cancel(self):
-        BuildConfig(self.env, name='foo', label='Foo', path='branches/foo',
-                    active=True).insert()
-
-        redirected_to = []
-        def redirect(url):
-            redirected_to.append(url)
-            raise RequestDone
-        req = Mock(method='POST', perm=PermissionCache(self.env, 'joe'),
-                   abs_href=Href('http://example.org/'), redirect=redirect,
-                   authname='joe',
-                   args={'cancel': '', 'new': '', 'name': 'Test',
-                         'property_0': 'family', 'pattern_0': 'posix'})
-
-        provider = BuildConfigurationsAdminPageProvider(self.env)
-        try:
-            provider.render_admin_panel(req, 'bitten', 'configs', 'foo')
-            self.fail('Expected RequestDone')
-
-        except RequestDone:
-            self.assertEqual('http://example.org/admin/bitten/configs/foo',
-                             redirected_to[0])
-            platforms = list(TargetPlatform.select(self.env, config='foo'))
-            self.assertEqual(0, len(platforms))
-
-    def test_process_remove_platforms(self):
-        BuildConfig(self.env, name='foo', label='Foo', path='branches/foo',
-                    active=True).insert()
-        platform = TargetPlatform(self.env, config='foo', name='any')
-        platform.insert()
-
-        redirected_to = []
-        def redirect(url):
-            redirected_to.append(url)
-            raise RequestDone
-        req = Mock(method='POST', perm=PermissionCache(self.env, 'joe'),
-                   abs_href=Href('http://example.org/'), redirect=redirect,
-                   authname='joe',
-                   args={'remove': '', 'sel': str(platform.id)})
-
-        provider = BuildConfigurationsAdminPageProvider(self.env)
-        try:
-            provider.render_admin_panel(req, 'bitten', 'configs', 'foo')
-            self.fail('Expected RequestDone')
-
-        except RequestDone:
-            self.assertEqual('http://example.org/admin/bitten/configs/foo',
-                             redirected_to[0])
-            platforms = list(TargetPlatform.select(self.env, config='foo'))
-            self.assertEqual(0, len(platforms))
-
-    def test_process_remove_platforms_no_selection(self):
-        BuildConfig(self.env, name='foo', label='Foo', path='branches/foo',
-                    active=True).insert()
-        platform = TargetPlatform(self.env, config='foo', name='any')
-        platform.insert()
-
-        redirected_to = []
-        def redirect(url):
-            redirected_to.append(url)
-            raise RequestDone
-        req = Mock(method='POST', perm=PermissionCache(self.env, 'joe'),
-                   abs_href=Href('http://example.org/'), redirect=redirect,
-                   authname='joe',
-                   args={'remove': ''})
-
-        provider = BuildConfigurationsAdminPageProvider(self.env)
-        try:
-            provider.render_admin_panel(req, 'bitten', 'configs', 'foo')
-            self.fail('Expected TracError')
-
-        except TracError, e:
-            self.assertEqual('No platform selected', e.message)
-
-    def test_process_edit_platform(self):
-        BuildConfig(self.env, name='foo', label='Foo', path='branches/foo',
-                    active=True).insert()
-        platform = TargetPlatform(self.env, config='foo', name='any')
-        platform.insert()
-
-        req = Mock(method='GET', chrome={}, href=Href('/'),
-                   perm=PermissionCache(self.env, 'joe'), args={})
-
-        provider = BuildConfigurationsAdminPageProvider(self.env)
-        template_name, data = provider.render_admin_panel(
-            req, 'bitten', 'configs', 'foo/%d' % platform.id
-        )
-
-        self.assertEqual('bitten_admin_configs.html', template_name)
-        assert 'platform' in data
-        platform = data['platform']
-        self.assertEqual({
-            'id': 1, 'exists': True, 'name': 'any', 'rules': [('', '')],
-        }, platform)
-
-    def test_process_update_platform(self):
-        BuildConfig(self.env, name='foo', label='Foo', path='branches/foo',
-                    active=True).insert()
-        platform = TargetPlatform(self.env, config='foo', name='any')
-        platform.insert()
-
-        redirected_to = []
-        def redirect(url):
-            redirected_to.append(url)
-            raise RequestDone
-        req = Mock(method='POST', perm=PermissionCache(self.env, 'joe'),
-                   abs_href=Href('http://example.org/'), redirect=redirect,
-                   authname='joe',
-                   args={'save': '', 'edit': '', 'name': 'Test',
-                         'property_0': 'family', 'pattern_0': 'posix'})
-
-        provider = BuildConfigurationsAdminPageProvider(self.env)
-        try:
-            provider.render_admin_panel(req, 'bitten', 'configs',
-                                           'foo/%d' % platform.id)
-            self.fail('Expected RequestDone')
-
-        except RequestDone:
-            self.assertEqual('http://example.org/admin/bitten/configs/foo',
-                             redirected_to[0])
-            platforms = list(TargetPlatform.select(self.env, config='foo'))
-            self.assertEqual(1, len(platforms))
-            self.assertEqual('Test', platforms[0].name)
-            self.assertEqual([('family', 'posix')], platforms[0].rules)
-
-    def test_process_update_platform_cancel(self):
-        BuildConfig(self.env, name='foo', label='Foo', path='branches/foo',
-                    active=True).insert()
-        platform = TargetPlatform(self.env, config='foo', name='any')
-        platform.insert()
-
-        redirected_to = []
-        def redirect(url):
-            redirected_to.append(url)
-            raise RequestDone
-        req = Mock(method='POST', perm=PermissionCache(self.env, 'joe'),
-                   abs_href=Href('http://example.org/'), redirect=redirect,
-                   authname='joe',
-                   args={'cancel': '', 'edit': '', 'name': 'Changed',
-                         'property_0': 'family', 'pattern_0': 'posix'})
-
-        provider = BuildConfigurationsAdminPageProvider(self.env)
-        try:
-            provider.render_admin_panel(req, 'bitten', 'configs',
-                                           'foo/%d' % platform.id)
-            self.fail('Expected RequestDone')
-
-        except RequestDone:
-            self.assertEqual('http://example.org/admin/bitten/configs/foo',
-                             redirected_to[0])
-            platforms = list(TargetPlatform.select(self.env, config='foo'))
-            self.assertEqual(1, len(platforms))
-            self.assertEqual('any', platforms[0].name)
-            self.assertEqual([], platforms[0].rules)
-
-
-def suite():
-    suite = unittest.TestSuite()
-    suite.addTest(unittest.makeSuite(
-        BuildMasterAdminPageProviderTestCase, 'test'
-    ))
-    suite.addTest(unittest.makeSuite(
-        BuildConfigurationsAdminPageProviderTestCase, 'test'
-    ))
-    return suite
-
-if __name__ == '__main__':
-    unittest.main(defaultTest='suite')
deleted file mode 100644
--- a/trac-0.11/bitten/tests/master.py
+++ /dev/null
@@ -1,748 +0,0 @@
-# -*- 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 re
-import shutil
-from StringIO import StringIO
-import tempfile
-import unittest
-
-from trac.db import DatabaseManager
-from trac.perm import PermissionCache, PermissionSystem
-from trac.test import EnvironmentStub, Mock
-from trac.web.api import HTTPBadRequest, HTTPMethodNotAllowed, HTTPNotFound, \
-                         HTTPForbidden, RequestDone
-from trac.web.href import Href
-
-from bitten.master import BuildMaster
-from bitten.model import BuildConfig, TargetPlatform, Build, BuildStep, \
-                         BuildLog, Report, schema
-
-
-class BuildMasterTestCase(unittest.TestCase):
-
-    def setUp(self):
-        self.env = EnvironmentStub(enable=['trac.*', 'bitten.*'])
-        self.env.path = tempfile.mkdtemp()
-
-        PermissionSystem(self.env).grant_permission('hal', 'BUILD_EXEC')
-
-        # Create tables
-        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)
-
-        self.repos = Mock()
-        self.env.get_repository = lambda authname=None: self.repos
-
-    def tearDown(self):
-        shutil.rmtree(self.env.path)
-
-    def test_create_build(self):
-        BuildConfig(self.env, 'test', path='somepath', active=True).insert()
-        platform = TargetPlatform(self.env, config='test', name="Unix")
-        platform.rules.append(('family', 'posix'))
-        platform.insert()
-
-        self.repos = Mock(
-            get_node=lambda path, rev=None: Mock(
-                get_entries=lambda: [Mock(), Mock()],
-                get_history=lambda: [('somepath', 123, 'edit'),
-                                     ('somepath', 121, 'edit'),
-                                     ('somepath', 120, 'edit')]
-            ),
-            get_changeset=lambda rev: Mock(date=42),
-            normalize_path=lambda path: path,
-            rev_older_than=lambda rev1, rev2: rev1 < rev2
-        )
-
-        inheaders = {'Content-Type': 'application/x-bitten+xml'}
-        inbody = StringIO("""<slave name="hal">
-  <platform>Power Macintosh</platform>
-  <os family="posix" version="8.1.0">Darwin</os>
-  <package name="java" version="2.4.3"/>
-</slave>""")
-        outheaders = {}
-        outbody = StringIO()
-        req = Mock(method='POST', base_path='', path_info='/builds',
-                   href=Href('/trac'), abs_href=Href('http://example.org/trac'),
-                   remote_addr='127.0.0.1', args={},
-                   perm=PermissionCache(self.env, 'hal'),
-                   get_header=lambda x: inheaders.get(x), read=inbody.read,
-                   send_response=lambda x: outheaders.setdefault('Status', x),
-                   send_header=lambda x, y: outheaders.setdefault(x, y),
-                   write=outbody.write)
-
-        module = BuildMaster(self.env)
-        assert module.match_request(req)
-        try:
-            module.process_request(req)
-            self.fail('Expected RequestDone')
-        except RequestDone:
-            self.assertEqual(201, outheaders['Status'])
-            self.assertEqual('text/plain', outheaders['Content-Type'])
-            location = outheaders['Location']
-            mo = re.match('http://example.org/trac/builds/(\d+)', location)
-            assert mo, 'Location was %r' % location
-            self.assertEqual('Build pending', outbody.getvalue())
-            build = Build.fetch(self.env, int(mo.group(1)))
-            self.assertEqual(Build.IN_PROGRESS, build.status)
-            self.assertEqual('hal', build.slave)
-
-    def test_create_build_invalid_xml(self):
-        inheaders = {'Content-Type': 'application/x-bitten+xml'}
-        inbody = StringIO('<slave></salve>')
-        req = Mock(method='POST', base_path='', path_info='/builds',
-                   href=Href('/trac'), remote_addr='127.0.0.1', args={},
-                   perm=PermissionCache(self.env, 'hal'),
-                   get_header=lambda x: inheaders.get(x), read=inbody.read)
-
-        module = BuildMaster(self.env)
-        assert module.match_request(req)
-        try:
-            module.process_request(req)
-            self.fail('Expected HTTPBadRequest')
-        except HTTPBadRequest, e:
-            self.assertEqual('XML parser error', e.detail)
-
-    def test_create_build_no_post(self):
-        req = Mock(method='GET', base_path='', path_info='/builds',
-                   href=Href('/trac'), remote_addr='127.0.0.1', args={},
-                   perm=PermissionCache(self.env, 'hal'))
-        module = BuildMaster(self.env)
-        assert module.match_request(req)
-        try:
-            module.process_request(req)
-            self.fail('Expected HTTPMethodNotAllowed')
-        except HTTPMethodNotAllowed, e:
-            self.assertEqual('Method not allowed', e.detail)
-
-    def test_create_build_no_match(self):
-        inheaders = {'Content-Type': 'application/x-bitten+xml'}
-        inbody = StringIO("""<slave name="hal">
-  <platform>Power Macintosh</platform>
-  <os family="posix" version="8.1.0">Darwin</os>
-</slave>""")
-        outheaders = {}
-        outbody = StringIO()
-        req = Mock(method='POST', base_path='', path_info='/builds',
-                   href=Href('/trac'), remote_addr='127.0.0.1', args={},
-                   perm=PermissionCache(self.env, 'hal'),
-                   get_header=lambda x: inheaders.get(x), read=inbody.read,
-                   send_response=lambda x: outheaders.setdefault('Status', x),
-                   send_header=lambda x, y: outheaders.setdefault(x, y),
-                   write=outbody.write)
-
-        module = BuildMaster(self.env)
-        assert module.match_request(req)
-        try:
-            module.process_request(req)
-            self.fail('Expected RequestDone')
-        except RequestDone:
-            self.assertEqual(204, outheaders['Status'])
-            self.assertEqual('', outbody.getvalue())
-
-    def test_cancel_build(self):
-        config = BuildConfig(self.env, 'test', path='somepath', active=True,
-                             recipe='<build></build>')
-        config.insert()
-        build = Build(self.env, 'test', '123', 1, slave='hal', rev_time=42,
-                      status=Build.IN_PROGRESS, started=42)
-        build.insert()
-
-        outheaders = {}
-        outbody = StringIO()
-        req = Mock(method='DELETE', base_path='',
-                   path_info='/builds/%d' % build.id,
-                   href=Href('/trac'), remote_addr='127.0.0.1', args={},
-                   perm=PermissionCache(self.env, 'hal'),
-                   send_response=lambda x: outheaders.setdefault('Status', x),
-                   send_header=lambda x, y: outheaders.setdefault(x, y),
-                   write=outbody.write)
-
-        module = BuildMaster(self.env)
-        assert module.match_request(req)
-        try:
-            module.process_request(req)
-            self.fail('Expected RequestDone')
-        except RequestDone:
-            self.assertEqual(204, outheaders['Status'])
-            self.assertEqual('', outbody.getvalue())
-
-            # Make sure the started timestamp has been set
-            build = Build.fetch(self.env, build.id)
-            self.assertEqual(Build.PENDING, build.status)
-            assert not build.started
-
-    def test_initiate_build(self):
-        config = BuildConfig(self.env, 'test', path='somepath', active=True,
-                             recipe='<build></build>')
-        config.insert()
-        platform = TargetPlatform(self.env, config='test', name="Unix")
-        platform.rules.append(('family', 'posix'))
-        platform.insert()
-        build = Build(self.env, 'test', '123', platform.id, slave='hal',
-                      rev_time=42)
-        build.insert()
-
-        outheaders = {}
-        outbody = StringIO()
-        
-        req = Mock(method='GET', base_path='',
-                   path_info='/builds/%d' % build.id,
-                   href=Href('/trac'), remote_addr='127.0.0.1', args={},
-                   perm=PermissionCache(self.env, 'hal'),
-                   send_response=lambda x: outheaders.setdefault('Status', x),
-                   send_header=lambda x, y: outheaders.setdefault(x, y),
-                   write=outbody.write)
-
-        module = BuildMaster(self.env)
-        assert module.match_request(req)
-        try:
-            module.process_request(req)
-            self.fail('Expected RequestDone')
-        except RequestDone:
-            self.assertEqual(200, outheaders['Status'])
-            self.assertEqual('63', outheaders['Content-Length'])
-            self.assertEqual('application/x-bitten+xml',
-                             outheaders['Content-Type'])
-            self.assertEqual('attachment; filename=recipe_test_r123.xml',
-                             outheaders['Content-Disposition'])
-            self.assertEqual('<build build="1" config="test"'
-                             ' path="somepath" revision="123"/>',
-                             outbody.getvalue())
-
-            # Make sure the started timestamp has been set
-            build = Build.fetch(self.env, build.id)
-            assert build.started
-
-    def test_initiate_build_no_such_build(self):
-        req = Mock(method='GET', base_path='',
-                   path_info='/builds/123', href=Href('/trac'),
-                   remote_addr='127.0.0.1', args={},
-                   perm=PermissionCache(self.env, 'hal'))
-
-        module = BuildMaster(self.env)
-        assert module.match_request(req)
-        try:
-            module.process_request(req)
-            self.fail('Expected HTTPNotFound')
-        except HTTPNotFound, e:
-            self.assertEqual('No such build', e.detail)
-
-    def test_process_unknown_collection(self):
-        BuildConfig(self.env, 'test', path='somepath', active=True,
-                    recipe='<build></build>').insert()
-        build = Build(self.env, 'test', '123', 1, slave='hal', rev_time=42)
-        build.insert()
-
-        req = Mock(method='POST', base_path='',
-                   path_info='/builds/%d/files/' % build.id,
-                   href=Href('/trac'), remote_addr='127.0.0.1', args={},
-                   perm=PermissionCache(self.env, 'hal'))
-
-        module = BuildMaster(self.env)
-        assert module.match_request(req)
-        try:
-            module.process_request(req)
-            self.fail('Expected HTTPNotFound')
-        except HTTPNotFound, e:
-            self.assertEqual('No such collection', e.detail)
-
-    def test_process_build_step_success(self):
-        recipe = """<build>
-  <step id="foo">
-  </step>
-</build>"""
-        BuildConfig(self.env, 'test', path='somepath', active=True,
-                    recipe=recipe).insert()
-        build = Build(self.env, 'test', '123', 1, slave='hal', rev_time=42,
-                      started=42, status=Build.IN_PROGRESS)
-        build.slave_info[Build.IP_ADDRESS] = '127.0.0.1';
-        build.insert()
-
-        inbody = StringIO("""<result step="foo" status="success"
-                                     time="2007-04-01T15:30:00.0000"
-                                     duration="3.45">
-</result>""")
-        outheaders = {}
-        outbody = StringIO()
-        req = Mock(method='POST', base_path='',
-                   path_info='/builds/%d/steps/' % build.id,
-                   href=Href('/trac'), abs_href=Href('http://example.org/trac'),
-                   remote_addr='127.0.0.1', args={},
-                   perm=PermissionCache(self.env, 'hal'),
-                   read=inbody.read,
-                   send_response=lambda x: outheaders.setdefault('Status', x),
-                   send_header=lambda x, y: outheaders.setdefault(x, y),
-                   write=outbody.write)
-        module = BuildMaster(self.env)
-        assert module.match_request(req)
-        try:
-            module.process_request(req)
-            self.fail('Expected RequestDone')
-        except RequestDone:
-            self.assertEqual(201, outheaders['Status'])
-            self.assertEqual('20', outheaders['Content-Length'])
-            self.assertEqual('text/plain', outheaders['Content-Type'])
-            self.assertEqual('Build step processed', outbody.getvalue())
-
-            build = Build.fetch(self.env, build.id)
-            self.assertEqual(Build.SUCCESS, build.status)
-            assert build.stopped
-            assert build.stopped > build.started
-
-            steps = list(BuildStep.select(self.env, build.id))
-            self.assertEqual(1, len(steps))
-            self.assertEqual('foo', steps[0].name)
-            self.assertEqual(BuildStep.SUCCESS, steps[0].status)
-
-    def test_process_build_step_success_with_log(self):
-        recipe = """<build>
-  <step id="foo">
-  </step>
-</build>"""
-        BuildConfig(self.env, 'test', path='somepath', active=True,
-                    recipe=recipe).insert()
-        build = Build(self.env, 'test', '123', 1, slave='hal', rev_time=42,
-                      started=42, status=Build.IN_PROGRESS)
-        build.slave_info[Build.IP_ADDRESS] = '127.0.0.1';
-        build.insert()
-
-        inbody = StringIO("""<result step="foo" status="success"
-                                     time="2007-04-01T15:30:00.0000"
-                                     duration="3.45">
-    <log generator="http://bitten.cmlenz.net/tools/python#unittest">
-        <message level="info">Doing stuff</message>
-        <message level="error">Ouch that hurt</message>
-    </log>
-</result>""")
-        outheaders = {}
-        outbody = StringIO()
-        req = Mock(method='POST', base_path='',
-                   path_info='/builds/%d/steps/' % build.id,
-                   href=Href('/trac'), abs_href=Href('http://example.org/trac'),
-                   remote_addr='127.0.0.1', args={},
-                   perm=PermissionCache(self.env, 'hal'),
-                   read=inbody.read,
-                   send_response=lambda x: outheaders.setdefault('Status', x),
-                   send_header=lambda x, y: outheaders.setdefault(x, y),
-                   write=outbody.write)
-        module = BuildMaster(self.env)
-        assert module.match_request(req)
-        try:
-            module.process_request(req)
-            self.fail('Expected RequestDone')
-        except RequestDone:
-            self.assertEqual(201, outheaders['Status'])
-            self.assertEqual('20', outheaders['Content-Length'])
-            self.assertEqual('text/plain', outheaders['Content-Type'])
-            self.assertEqual('Build step processed', outbody.getvalue())
-
-            build = Build.fetch(self.env, build.id)
-            self.assertEqual(Build.SUCCESS, build.status)
-            assert build.stopped
-            assert build.stopped > build.started
-
-            steps = list(BuildStep.select(self.env, build.id))
-            self.assertEqual(1, len(steps))
-            self.assertEqual('foo', steps[0].name)
-            self.assertEqual(BuildStep.SUCCESS, steps[0].status)
-
-            logs = list(BuildLog.select(self.env, build=build.id, step='foo'))
-            self.assertEqual(1, len(logs))
-            self.assertEqual('http://bitten.cmlenz.net/tools/python#unittest',
-                             logs[0].generator)
-            self.assertEqual(2, len(logs[0].messages))
-            self.assertEqual((u'info', u'Doing stuff'), logs[0].messages[0])
-            self.assertEqual((u'error', u'Ouch that hurt'), logs[0].messages[1])
-
-    def test_process_build_step_success_with_report(self):
-        recipe = """<build>
-  <step id="foo">
-  </step>
-</build>"""
-        BuildConfig(self.env, 'test', path='somepath', active=True,
-                    recipe=recipe).insert()
-        build = Build(self.env, 'test', '123', 1, slave='hal', rev_time=42,
-                      started=42, status=Build.IN_PROGRESS)
-        build.slave_info[Build.IP_ADDRESS] = '127.0.0.1';
-        build.insert()
-
-        inbody = StringIO("""<result step="foo" status="success"
-                                     time="2007-04-01T15:30:00.0000"
-                                     duration="3.45">
-    <report category="test"
-            generator="http://bitten.cmlenz.net/tools/python#unittest">
-        <test fixture="my.Fixture" file="my/test/file.py">
-            <stdout>Doing my thing</stdout>
-        </test>
-    </report>
-</result>""")
-        outheaders = {}
-        outbody = StringIO()
-        req = Mock(method='POST', base_path='',
-                   path_info='/builds/%d/steps/' % build.id,
-                   href=Href('/trac'), abs_href=Href('http://example.org/trac'),
-                   remote_addr='127.0.0.1', args={},
-                   perm=PermissionCache(self.env, 'hal'),
-                   read=inbody.read,
-                   send_response=lambda x: outheaders.setdefault('Status', x),
-                   send_header=lambda x, y: outheaders.setdefault(x, y),
-                   write=outbody.write)
-        module = BuildMaster(self.env)
-        assert module.match_request(req)
-        try:
-            module.process_request(req)
-            self.fail('Expected RequestDone')
-        except RequestDone:
-            self.assertEqual(201, outheaders['Status'])
-            self.assertEqual('20', outheaders['Content-Length'])
-            self.assertEqual('text/plain', outheaders['Content-Type'])
-            self.assertEqual('Build step processed', outbody.getvalue())
-
-            build = Build.fetch(self.env, build.id)
-            self.assertEqual(Build.SUCCESS, build.status)
-            assert build.stopped
-            assert build.stopped > build.started
-
-            steps = list(BuildStep.select(self.env, build.id))
-            self.assertEqual(1, len(steps))
-            self.assertEqual('foo', steps[0].name)
-            self.assertEqual(BuildStep.SUCCESS, steps[0].status)
-
-            reports = list(Report.select(self.env, build=build.id, step='foo'))
-            self.assertEqual(1, len(reports))
-            self.assertEqual('test', reports[0].category)
-            self.assertEqual('http://bitten.cmlenz.net/tools/python#unittest',
-                             reports[0].generator)
-            self.assertEqual(1, len(reports[0].items))
-            self.assertEqual({
-                'fixture': 'my.Fixture', 
-                'file': 'my/test/file.py', 
-                'stdout': 'Doing my thing',
-                'type': 'test',
-            }, reports[0].items[0])
-
-    def test_process_build_step_wrong_slave(self):
-        recipe = """<build>
-  <step id="foo">
-  </step>
-</build>"""
-        BuildConfig(self.env, 'test', path='somepath', active=True,
-                    recipe=recipe).insert()
-        build = Build(self.env, 'test', '123', 1, slave='hal', rev_time=42,
-                      started=42, status=Build.IN_PROGRESS)
-        build.slave_info[Build.IP_ADDRESS] = '192.168.1.1';
-        build.insert()
-
-        inbody = StringIO("""<result step="foo" status="success"
-                                     time="2007-04-01T15:30:00.0000"
-                                     duration="3.45">
-    <log generator="http://bitten.cmlenz.net/tools/python#unittest">
-        <message level="info">Doing stuff</message>
-        <message level="error">Ouch that hurt</message>
-    </log>
-</result>""")
-        outheaders = {}
-        outbody = StringIO()
-        req = Mock(method='POST', base_path='',
-                   path_info='/builds/%d/steps/' % build.id,
-                   href=Href('/trac'), abs_href=Href('http://example.org/trac'),
-                   remote_addr='127.0.0.1', args={},
-                   perm=PermissionCache(self.env, 'hal'),
-                   read=inbody.read,
-                   send_response=lambda x: outheaders.setdefault('Status', x),
-                   send_header=lambda x, y: outheaders.setdefault(x, y),
-                   write=outbody.write)
-        module = BuildMaster(self.env)
-        assert module.match_request(req)
-        try:
-            module.process_request(req)
-            self.fail('Expected HTTPForbidden')
-        except HTTPForbidden, e:
-            self.assertEqual('Build 1 has been invalidated for host 127.0.0.1.', e.detail)
-
-            build = Build.fetch(self.env, build.id)
-            self.assertEqual(Build.IN_PROGRESS, build.status)
-            assert not build.stopped
-
-            steps = list(BuildStep.select(self.env, build.id))
-            self.assertEqual(0, len(steps))
-
-
-    def test_process_build_step_invalidated_build(self):
-        recipe = """<build>
-  <step id="foo">
-  </step>
-  <step id="foo2">
-  </step>
-</build>"""
-        BuildConfig(self.env, 'test', path='somepath', active=True,
-                    recipe=recipe).insert()
-        build = Build(self.env, 'test', '123', 1, slave='hal', rev_time=42,
-                      started=42, status=Build.IN_PROGRESS)
-        build.slave_info[Build.IP_ADDRESS] = '127.0.0.1';
-        build.insert()
-
-        inbody = StringIO("""<result step="foo" status="success"
-                                     time="2007-04-01T15:30:00.0000"
-                                     duration="3.45">
-    <log generator="http://bitten.cmlenz.net/tools/python#unittest">
-        <message level="info">Doing stuff</message>
-        <message level="error">Ouch that hurt</message>
-    </log>
-</result>""")
-        outheaders = {}
-        outbody = StringIO()
-        req = Mock(method='POST', base_path='',
-                   path_info='/builds/%d/steps/' % build.id,
-                   href=Href('/trac'), abs_href=Href('http://example.org/trac'),
-                   remote_addr='127.0.0.1', args={},
-                   perm=PermissionCache(self.env, 'hal'),
-                   read=inbody.read,
-                   send_response=lambda x: outheaders.setdefault('Status', x),
-                   send_header=lambda x, y: outheaders.setdefault(x, y),
-                   write=outbody.write)
-        module = BuildMaster(self.env)
-        assert module.match_request(req)
-        try:
-            module.process_request(req)
-            self.fail('Expected RequestDone')
-        except RequestDone:            
-            build = Build.fetch(self.env, build.id)
-            self.assertEqual(Build.IN_PROGRESS, build.status)
-            assert not build.stopped
-
-            steps = list(BuildStep.select(self.env, build.id))
-            self.assertEqual(1, len(steps))
-
-        # invalidate the build. 
-
-        build = Build.fetch(self.env, build.id)
-        build.slave = None
-        build.status = Build.PENDING
-        build.update()
-
-        # have this slave submit more data.
-        inbody = StringIO("""<result step="foo2" status="success"
-                                     time="2007-04-01T15:45:00.0000"
-                                     duration="4">
-    <log generator="http://bitten.cmlenz.net/tools/python#unittest">
-        <message level="info">This is a step after invalidation</message>
-    </log>
-</result>""")
-        outheaders = {}
-        outbody = StringIO()
-        req = Mock(method='POST', base_path='',
-                   path_info='/builds/%d/steps/' % build.id,
-                   href=Href('/trac'), abs_href=Href('http://example.org/trac'),
-                   remote_addr='127.0.0.1', args={},
-                   perm=PermissionCache(self.env, 'hal'),
-                   read=inbody.read,
-                   send_response=lambda x: outheaders.setdefault('Status', x),
-                   send_header=lambda x, y: outheaders.setdefault(x, y),
-                   write=outbody.write)
-        module = BuildMaster(self.env)
-        assert module.match_request(req)
-        try:
-            module.process_request(req)
-            self.fail('Build was invalidated. Should fail.');
-        except HTTPForbidden, e:
-            self.assertEqual('Build 1 has been invalidated for host 127.0.0.1.', e.detail)            
-
-            build = Build.fetch(self.env, build.id)
-            self.assertEqual(Build.PENDING, build.status)
-
-    def test_process_build_step_failure(self):
-        recipe = """<build>
-  <step id="foo">
-  </step>
-</build>"""
-        BuildConfig(self.env, 'test', path='somepath', active=True,
-                    recipe=recipe).insert()
-        build = Build(self.env, 'test', '123', 1, slave='hal', rev_time=42,
-                      started=42, status=Build.IN_PROGRESS)
-        build.slave_info[Build.IP_ADDRESS] = '127.0.0.1';
-        build.insert()
-
-        inbody = StringIO("""<result step="foo" status="failure"
-                                     time="2007-04-01T15:30:00.0000"
-                                     duration="3.45">
-</result>""")
-        outheaders = {}
-        outbody = StringIO()
-        req = Mock(method='POST', base_path='',
-                   path_info='/builds/%d/steps/' % build.id,
-                   href=Href('/trac'), abs_href=Href('http://example.org/trac'),
-                   remote_addr='127.0.0.1', args={},
-                   perm=PermissionCache(self.env, 'hal'),
-                   read=inbody.read,
-                   send_response=lambda x: outheaders.setdefault('Status', x),
-                   send_header=lambda x, y: outheaders.setdefault(x, y),
-                   write=outbody.write)
-        module = BuildMaster(self.env)
-        assert module.match_request(req)
-        try:
-            module.process_request(req)
-            self.fail('Expected RequestDone')
-        except RequestDone:
-            self.assertEqual(201, outheaders['Status'])
-            self.assertEqual('20', outheaders['Content-Length'])
-            self.assertEqual('text/plain', outheaders['Content-Type'])
-            self.assertEqual('Build step processed', outbody.getvalue())
-
-            build = Build.fetch(self.env, build.id)
-            self.assertEqual(Build.FAILURE, build.status)
-            assert build.stopped
-            assert build.stopped > build.started
-
-            steps = list(BuildStep.select(self.env, build.id))
-            self.assertEqual(1, len(steps))
-            self.assertEqual('foo', steps[0].name)
-            self.assertEqual(BuildStep.FAILURE, steps[0].status)
-
-    def test_process_build_step_failure_ignored(self):
-        recipe = """<build>
-  <step id="foo" onerror="ignore">
-  </step>
-</build>"""
-        BuildConfig(self.env, 'test', path='somepath', active=True,
-                    recipe=recipe).insert()
-        build = Build(self.env, 'test', '123', 1, slave='hal', rev_time=42,
-                      started=42, status=Build.IN_PROGRESS)
-        build.slave_info[Build.IP_ADDRESS] = '127.0.0.1';
-
-        build.insert()
-
-        inbody = StringIO("""<result step="foo" status="failure"
-                                     time="2007-04-01T15:30:00.0000"
-                                     duration="3.45">
-</result>""")
-        outheaders = {}
-        outbody = StringIO()
-        req = Mock(method='POST', base_path='',
-                   path_info='/builds/%d/steps/' % build.id,
-                   href=Href('/trac'), abs_href=Href('http://example.org/trac'),
-                   remote_addr='127.0.0.1', args={},
-                   perm=PermissionCache(self.env, 'hal'),
-                   read=inbody.read,
-                   send_response=lambda x: outheaders.setdefault('Status', x),
-                   send_header=lambda x, y: outheaders.setdefault(x, y),
-                   write=outbody.write)
-        module = BuildMaster(self.env)
-        assert module.match_request(req)
-        try:
-            module.process_request(req)
-            self.fail('Expected RequestDone')
-        except RequestDone:
-            self.assertEqual(201, outheaders['Status'])
-            self.assertEqual('20', outheaders['Content-Length'])
-            self.assertEqual('text/plain', outheaders['Content-Type'])
-            self.assertEqual('Build step processed', outbody.getvalue())
-
-            build = Build.fetch(self.env, build.id)
-            self.assertEqual(Build.SUCCESS, build.status)
-            assert build.stopped
-            assert build.stopped > build.started
-
-            steps = list(BuildStep.select(self.env, build.id))
-            self.assertEqual(1, len(steps))
-            self.assertEqual('foo', steps[0].name)
-            self.assertEqual(BuildStep.FAILURE, steps[0].status)
-
-    def test_process_build_step_invalid_xml(self):
-        recipe = """<build>
-  <step id="foo">
-  </step>
-</build>"""
-        BuildConfig(self.env, 'test', path='somepath', active=True,
-                    recipe=recipe).insert()
-        build = Build(self.env, 'test', '123', 1, slave='hal', rev_time=42,
-                      started=42)
-        build.insert()
-
-        inbody = StringIO("""<result></rsleut>""")
-        req = Mock(method='POST', base_path='',
-                   path_info='/builds/%d/steps/' % build.id,
-                   href=Href('/trac'), remote_addr='127.0.0.1', args={},
-                   perm=PermissionCache(self.env, 'hal'),
-                   read=inbody.read)
-
-        module = BuildMaster(self.env)
-        assert module.match_request(req)
-        try:
-            module.process_request(req)
-            self.fail('Expected HTTPBadRequest')
-        except HTTPBadRequest, e:
-            self.assertEqual('XML parser error', e.detail)
-
-    def test_process_build_step_invalid_datetime(self):
-        recipe = """<build>
-  <step id="foo">
-  </step>
-</build>"""
-        BuildConfig(self.env, 'test', path='somepath', active=True,
-                    recipe=recipe).insert()
-        build = Build(self.env, 'test', '123', 1, slave='hal', rev_time=42,
-                      started=42, status=Build.IN_PROGRESS)
-        build.slave_info[Build.IP_ADDRESS] = '127.0.0.1';
-        build.insert()
-
-        inbody = StringIO("""<result step="foo" status="success"
-                                     time="sometime tomorrow maybe"
-                                     duration="3.45">
-</result>""")
-        req = Mock(method='POST', base_path='',
-                   path_info='/builds/%d/steps/' % build.id,
-                   href=Href('/trac'), remote_addr='127.0.0.1', args={},
-                   perm=PermissionCache(self.env, 'hal'),
-                   read=inbody.read)
-
-        module = BuildMaster(self.env)
-        assert module.match_request(req)
-        try:
-            module.process_request(req)
-            self.fail('Expected HTTPBadRequest')
-        except HTTPBadRequest, e:
-            self.assertEqual("Invalid ISO date/time 'sometime tomorrow maybe'",
-                             e.detail)
-
-    def test_process_build_step_no_post(self):
-        BuildConfig(self.env, 'test', path='somepath', active=True,
-                    recipe='<build></build>').insert()
-        build = Build(self.env, 'test', '123', 1, slave='hal', rev_time=42,
-                      started=42)
-        build.insert()
-
-        req = Mock(method='GET', base_path='',
-                   path_info='/builds/%d/steps/' % build.id,
-                   href=Href('/trac'), remote_addr='127.0.0.1', args={},
-                   perm=PermissionCache(self.env, 'hal'))
-
-        module = BuildMaster(self.env)
-        assert module.match_request(req)
-        try:
-            module.process_request(req)
-            self.fail('Expected HTTPMethodNotAllowed')
-        except HTTPMethodNotAllowed, e:
-            self.assertEqual('Method not allowed', e.detail)
-
-
-def suite():
-    suite = unittest.TestSuite()
-    suite.addTest(unittest.makeSuite(BuildMasterTestCase, 'test'))
-    return suite
-
-if __name__ == '__main__':
-    unittest.main(defaultTest='suite')
deleted file mode 100644
--- a/trac-0.11/bitten/tests/model.py
+++ /dev/null
@@ -1,733 +0,0 @@
-# -*- 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
-from bitten.model import BuildConfig, TargetPlatform, Build, BuildStep, \
-                         BuildLog, Report, schema
-
-
-class BuildConfigTestCase(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)
-        db.commit()
-
-    def test_new(self):
-        config = BuildConfig(self.env, name='test')
-        assert not config.exists
-
-    def test_fetch(self):
-        db = self.env.get_db_cnx()
-        cursor = db.cursor()
-        cursor.execute("INSERT INTO bitten_config (name,path,label,active) "
-                       "VALUES (%s,%s,%s,%s)", ('test', 'trunk', 'Test', 0))
-        config = BuildConfig.fetch(self.env, name='test')
-        assert config.exists
-        self.assertEqual('test', config.name)
-        self.assertEqual('trunk', config.path)
-        self.assertEqual('Test', config.label)
-        self.assertEqual(False, config.active)
-
-    def test_fetch_none(self):
-        config = BuildConfig.fetch(self.env, name='test')
-        self.assertEqual(None, config)
-
-    def test_select_none(self):
-        configs = BuildConfig.select(self.env)
-        self.assertRaises(StopIteration, configs.next)
-
-    def test_select_none(self):
-        configs = BuildConfig.select(self.env)
-        self.assertRaises(StopIteration, configs.next)
-
-    def test_insert(self):
-        config = BuildConfig(self.env, name='test', path='trunk', label='Test')
-        config.insert()
-
-        db = self.env.get_db_cnx()
-        cursor = db.cursor()
-        cursor.execute("SELECT name,path,label,active,description "
-                       "FROM bitten_config")
-        self.assertEqual(('test', 'trunk', 'Test', 0, ''), cursor.fetchone())
-
-    def test_insert_no_name(self):
-        config = BuildConfig(self.env)
-        self.assertRaises(AssertionError, config.insert)
-
-    def test_update(self):
-        db = self.env.get_db_cnx()
-        cursor = db.cursor()
-        cursor.execute("INSERT INTO bitten_config (name,path,label,active) "
-                       "VALUES (%s,%s,%s,%s)", ('test', 'trunk', 'Test', 0))
-
-        config = BuildConfig.fetch(self.env, 'test')
-        config.path = 'some_branch'
-        config.label = 'Updated'
-        config.active = True
-        config.description = 'Bla bla bla'
-        config.update()
-
-        db = self.env.get_db_cnx()
-        cursor = db.cursor()
-        cursor.execute("SELECT name,path,label,active,description "
-                       "FROM bitten_config")
-        self.assertEqual(('test', 'some_branch', 'Updated', 1, 'Bla bla bla'),
-                         cursor.fetchone())
-        self.assertEqual(None, cursor.fetchone())
-
-    def test_update_name(self):
-        db = self.env.get_db_cnx()
-        cursor = db.cursor()
-        cursor.execute("INSERT INTO bitten_config (name,path,label,active) "
-                       "VALUES (%s,%s,%s,%s)", ('test', 'trunk', 'Test', 0))
-
-        config = BuildConfig.fetch(self.env, 'test')
-        config.name = 'foobar'
-        config.update()
-
-        db = self.env.get_db_cnx()
-        cursor = db.cursor()
-        cursor.execute("SELECT name,path,label,active,description "
-                       "FROM bitten_config")
-        self.assertEqual(('foobar', 'trunk', 'Test', 0, ''), cursor.fetchone())
-        self.assertEqual(None, cursor.fetchone())
-
-    def test_update_no_name(self):
-        db = self.env.get_db_cnx()
-        cursor = db.cursor()
-        cursor.execute("INSERT INTO bitten_config (name,path,label,active) "
-                       "VALUES (%s,%s,%s,%s)", ('test', 'trunk', 'Test', 0))
-
-        config = BuildConfig.fetch(self.env, 'test')
-        config.name = None
-        self.assertRaises(AssertionError, config.update)
-
-    def test_update_name_with_platform(self):
-        db = self.env.get_db_cnx()
-        cursor = db.cursor()
-        cursor.execute("INSERT INTO bitten_config (name,path,label,active) "
-                       "VALUES (%s,%s,%s,%s)", ('test', 'trunk', 'Test', 0))
-        cursor.execute("INSERT INTO bitten_platform (config,name) "
-                       "VALUES (%s,%s)", ('test', 'NetBSD'))
-
-        config = BuildConfig.fetch(self.env, 'test')
-        config.name = 'foobar'
-        config.update()
-
-        cursor.execute("SELECT config,name FROM bitten_platform")
-        self.assertEqual(('foobar', 'NetBSD'), cursor.fetchone())
-        self.assertEqual(None, cursor.fetchone())
-
-    def test_delete(self):
-        db = self.env.get_db_cnx()
-        cursor = db.cursor()
-        cursor.execute("INSERT INTO bitten_config (name,path,label,active) "
-                       "VALUES (%s,%s,%s,%s)", ('test', 'trunk', 'Test', 0))
-
-        config = BuildConfig.fetch(self.env, 'test')
-        config.delete()
-        self.assertEqual(False, config.exists)
-
-        cursor.execute("SELECT * FROM bitten_config WHERE name=%s", ('test',))
-        self.assertEqual(None, cursor.fetchone())
-
-    def test_delete_non_existing(self):
-        config = BuildConfig(self.env, 'test')
-        self.assertRaises(AssertionError, config.delete)
-
-
-class TargetPlatformTestCase(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 TargetPlatform._schema:
-            for stmt in connector.to_sql(table):
-                cursor.execute(stmt)
-        db.commit()
-
-    def test_new(self):
-        platform = TargetPlatform(self.env)
-        self.assertEqual(False, platform.exists)
-        self.assertEqual([], platform.rules)
-
-    def test_insert(self):
-        platform = TargetPlatform(self.env, config='test', name='Windows XP')
-        platform.rules += [(Build.OS_NAME, 'Windows'), (Build.OS_VERSION, 'XP')]
-        platform.insert()
-
-        assert platform.exists
-        db = self.env.get_db_cnx()
-        cursor = db.cursor()
-        cursor.execute("SELECT config,name FROM bitten_platform "
-                       "WHERE id=%s", (platform.id,))
-        self.assertEqual(('test', 'Windows XP'), cursor.fetchone())
-
-        cursor.execute("SELECT propname,pattern,orderno FROM bitten_rule "
-                       "WHERE id=%s", (platform.id,))
-        self.assertEqual((Build.OS_NAME, 'Windows', 0), cursor.fetchone())
-        self.assertEqual((Build.OS_VERSION, 'XP', 1), cursor.fetchone())
-
-    def test_fetch(self):
-        db = self.env.get_db_cnx()
-        cursor = db.cursor()
-        cursor.execute("INSERT INTO bitten_platform (config,name) "
-                       "VALUES (%s,%s)", ('test', 'Windows'))
-        id = db.get_last_id(cursor, 'bitten_platform')
-        platform = TargetPlatform.fetch(self.env, id)
-        assert platform.exists
-        self.assertEqual('test', platform.config)
-        self.assertEqual('Windows', platform.name)
-
-    def test_select(self):
-        db = self.env.get_db_cnx()
-        cursor = db.cursor()
-        cursor.executemany("INSERT INTO bitten_platform (config,name) "
-                           "VALUES (%s,%s)", [('test', 'Windows'),
-                           ('test', 'Mac OS X')])
-        platforms = list(TargetPlatform.select(self.env, config='test'))
-        self.assertEqual(2, len(platforms))
-
-
-class BuildTestCase(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 Build._schema:
-            for stmt in connector.to_sql(table):
-                cursor.execute(stmt)
-        db.commit()
-
-    def test_new(self):
-        build = Build(self.env)
-        self.assertEqual(None, build.id)
-        self.assertEqual(Build.PENDING, build.status)
-        self.assertEqual(0, build.stopped)
-        self.assertEqual(0, build.started)
-
-    def test_insert(self):
-        build = Build(self.env, config='test', rev='42', rev_time=12039,
-                      platform=1)
-        build.slave_info.update({Build.IP_ADDRESS: '127.0.0.1',
-                                 Build.MAINTAINER: 'joe@example.org'})
-        build.insert()
-
-        db = self.env.get_db_cnx()
-        cursor = db.cursor()
-        cursor.execute("SELECT config,rev,platform,slave,started,stopped,status"
-                       " FROM bitten_build WHERE id=%s" % build.id)
-        self.assertEqual(('test', '42', 1, '', 0, 0, 'P'), cursor.fetchone())
-
-        cursor.execute("SELECT propname,propvalue FROM bitten_slave")
-        expected = {Build.IP_ADDRESS: '127.0.0.1',
-                    Build.MAINTAINER: 'joe@example.org'}
-        for propname, propvalue in cursor:
-            self.assertEqual(expected[propname], propvalue)
-
-    def test_insert_no_config_or_rev_or_rev_time_or_platform(self):
-        build = Build(self.env)
-        self.assertRaises(AssertionError, build.insert)
-
-        build = Build(self.env, rev='42', rev_time=12039, platform=1)
-        self.assertRaises(AssertionError, build.insert) # No config
-
-        build = Build(self.env, config='test', rev_time=12039, platform=1)
-        self.assertRaises(AssertionError, build.insert) # No rev
-
-        build = Build(self.env, config='test', rev='42', platform=1)
-        self.assertRaises(AssertionError, build.insert) # No rev time
-
-        build = Build(self.env, config='test', rev='42', rev_time=12039)
-        self.assertRaises(AssertionError, build.insert) # No platform
-
-    def test_insert_no_slave(self):
-        build = Build(self.env, config='test', rev='42', rev_time=12039,
-                      platform=1)
-        build.status = Build.SUCCESS
-        self.assertRaises(AssertionError, build.insert)
-        build.status = Build.FAILURE
-        self.assertRaises(AssertionError, build.insert)
-        build.status = Build.IN_PROGRESS
-        self.assertRaises(AssertionError, build.insert)
-        build.status = Build.PENDING
-        build.insert()
-
-    def test_insert_invalid_status(self):
-        build = Build(self.env, config='test', rev='42', rev_time=12039,
-                      status='DUNNO')
-        self.assertRaises(AssertionError, build.insert)
-
-    def test_fetch(self):
-        db = self.env.get_db_cnx()
-        cursor = db.cursor()
-        cursor.execute("INSERT INTO bitten_build (config,rev,rev_time,platform,"
-                       "slave,started,stopped,status) "
-                       "VALUES (%s,%s,%s,%s,%s,%s,%s,%s)",
-                       ('test', '42', 12039, 1, 'tehbox', 15006, 16007,
-                        Build.SUCCESS))
-        build_id = db.get_last_id(cursor, 'bitten_build')
-        cursor.executemany("INSERT INTO bitten_slave VALUES (%s,%s,%s)",
-                           [(build_id, Build.IP_ADDRESS, '127.0.0.1'),
-                            (build_id, Build.MAINTAINER, 'joe@example.org')])
-
-        build = Build.fetch(self.env, build_id)
-        self.assertEquals(build_id, build.id)
-        self.assertEquals('127.0.0.1', build.slave_info[Build.IP_ADDRESS])
-        self.assertEquals('joe@example.org', build.slave_info[Build.MAINTAINER])
-
-    def test_update(self):
-        db = self.env.get_db_cnx()
-        cursor = db.cursor()
-        cursor.execute("INSERT INTO bitten_build (config,rev,rev_time,platform,"
-                       "slave,started,stopped,status) "
-                       "VALUES (%s,%s,%s,%s,%s,%s,%s,%s)",
-                       ('test', '42', 12039, 1, 'tehbox', 15006, 16007,
-                        Build.SUCCESS))
-        build_id = db.get_last_id(cursor, 'bitten_build')
-        cursor.executemany("INSERT INTO bitten_slave VALUES (%s,%s,%s)",
-                           [(build_id, Build.IP_ADDRESS, '127.0.0.1'),
-                            (build_id, Build.MAINTAINER, 'joe@example.org')])
-
-        build = Build.fetch(self.env, build_id)
-        build.status = Build.FAILURE
-        build.update()
-
-
-class BuildStepTestCase(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 BuildStep._schema:
-            for stmt in connector.to_sql(table):
-                cursor.execute(stmt)
-        db.commit()
-
-    def test_new(self):
-        step = BuildStep(self.env)
-        self.assertEqual(False, step.exists)
-        self.assertEqual(None, step.build)
-        self.assertEqual(None, step.name)
-
-    def test_insert(self):
-        step = BuildStep(self.env, build=1, name='test', description='Foo bar',
-                         status=BuildStep.SUCCESS)
-        step.insert()
-        self.assertEqual(True, step.exists)
-
-        db = self.env.get_db_cnx()
-        cursor = db.cursor()
-        cursor.execute("SELECT build,name,description,status,started,stopped "
-                       "FROM bitten_step")
-        self.assertEqual((1, 'test', 'Foo bar', BuildStep.SUCCESS, 0, 0),
-                         cursor.fetchone())
-
-    def test_insert_with_errors(self):
-        step = BuildStep(self.env, build=1, name='test', description='Foo bar',
-                         status=BuildStep.SUCCESS)
-        step.errors += ['Foo', 'Bar']
-        step.insert()
-        self.assertEqual(True, step.exists)
-
-        db = self.env.get_db_cnx()
-        cursor = db.cursor()
-        cursor.execute("SELECT build,name,description,status,started,stopped "
-                       "FROM bitten_step")
-        self.assertEqual((1, 'test', 'Foo bar', BuildStep.SUCCESS, 0, 0),
-                         cursor.fetchone())
-        cursor.execute("SELECT message FROM bitten_error ORDER BY orderno")
-        self.assertEqual(('Foo',), cursor.fetchone())
-        self.assertEqual(('Bar',), cursor.fetchone())
-
-    def test_insert_no_build_or_name(self):
-        step = BuildStep(self.env, name='test')
-        self.assertRaises(AssertionError, step.insert) # No build
-
-        step = BuildStep(self.env, build=1)
-        self.assertRaises(AssertionError, step.insert) # No name
-
-    def test_fetch(self):
-        db = self.env.get_db_cnx()
-        cursor = db.cursor()
-        cursor.execute("INSERT INTO bitten_step VALUES (%s,%s,%s,%s,%s,%s)",
-                       (1, 'test', 'Foo bar', BuildStep.SUCCESS, 0, 0))
-
-        step = BuildStep.fetch(self.env, build=1, name='test')
-        self.assertEqual(1, step.build)
-        self.assertEqual('test', step.name)
-        self.assertEqual('Foo bar', step.description)
-        self.assertEqual(BuildStep.SUCCESS, step.status)
-
-    def test_fetch_with_errors(self):
-        db = self.env.get_db_cnx()
-        cursor = db.cursor()
-        cursor.execute("INSERT INTO bitten_step VALUES (%s,%s,%s,%s,%s,%s)",
-                       (1, 'test', 'Foo bar', BuildStep.SUCCESS, 0, 0))
-        cursor.executemany("INSERT INTO bitten_error VALUES (%s,%s,%s,%s)",
-                           [(1, 'test', 'Foo', 0), (1, 'test', 'Bar', 1)])
-
-        step = BuildStep.fetch(self.env, build=1, name='test')
-        self.assertEqual(1, step.build)
-        self.assertEqual('test', step.name)
-        self.assertEqual('Foo bar', step.description)
-        self.assertEqual(BuildStep.SUCCESS, step.status)
-        self.assertEqual(['Foo', 'Bar'], step.errors)
-
-    def test_select(self):
-        db = self.env.get_db_cnx()
-        cursor = db.cursor()
-        cursor.executemany("INSERT INTO bitten_step VALUES (%s,%s,%s,%s,%s,%s)",
-                           [(1, 'test', 'Foo bar', BuildStep.SUCCESS, 1, 2),
-                            (1, 'dist', 'Foo baz', BuildStep.FAILURE, 2, 3)])
-
-        steps = list(BuildStep.select(self.env, build=1))
-        self.assertEqual(1, steps[0].build)
-        self.assertEqual('test', steps[0].name)
-        self.assertEqual('Foo bar', steps[0].description)
-        self.assertEqual(BuildStep.SUCCESS, steps[0].status)
-        self.assertEqual(1, steps[1].build)
-        self.assertEqual('dist', steps[1].name)
-        self.assertEqual('Foo baz', steps[1].description)
-        self.assertEqual(BuildStep.FAILURE, steps[1].status)
-
-
-class BuildLogTestCase(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 BuildLog._schema:
-            for stmt in connector.to_sql(table):
-                cursor.execute(stmt)
-        db.commit()
-
-    def test_new(self):
-        log = BuildLog(self.env)
-        self.assertEqual(False, log.exists)
-        self.assertEqual(None, log.id)
-        self.assertEqual(None, log.build)
-        self.assertEqual(None, log.step)
-        self.assertEqual('', log.generator)
-        self.assertEqual([], log.messages)
-
-    def test_insert(self):
-        log = BuildLog(self.env, build=1, step='test', generator='distutils')
-        log.messages = [
-            (BuildLog.INFO, 'running tests'),
-            (BuildLog.ERROR, 'tests failed')
-        ]
-        log.insert()
-        self.assertNotEqual(None, log.id)
-
-        db = self.env.get_db_cnx()
-        cursor = db.cursor()
-        cursor.execute("SELECT build,step,generator FROM bitten_log "
-                       "WHERE id=%s", (log.id,))
-        self.assertEqual((1, 'test', 'distutils'), cursor.fetchone())
-        cursor.execute("SELECT level,message FROM bitten_log_message "
-                       "WHERE log=%s ORDER BY line", (log.id,))
-        self.assertEqual((BuildLog.INFO, 'running tests'), cursor.fetchone())
-        self.assertEqual((BuildLog.ERROR, 'tests failed'), cursor.fetchone())
-
-    def test_insert_empty(self):
-        log = BuildLog(self.env, build=1, step='test', generator='distutils')
-        log.messages = []
-        log.insert()
-        self.assertNotEqual(None, log.id)
-
-        db = self.env.get_db_cnx()
-        cursor = db.cursor()
-        cursor.execute("SELECT build,step,generator FROM bitten_log "
-                       "WHERE id=%s", (log.id,))
-        self.assertEqual((1, 'test', 'distutils'), cursor.fetchone())
-        cursor.execute("SELECT COUNT(*) FROM bitten_log_message "
-                       "WHERE log=%s", (log.id,))
-        self.assertEqual(0, cursor.fetchone()[0])
-
-    def test_insert_no_build_or_step(self):
-        log = BuildLog(self.env, step='test')
-        self.assertRaises(AssertionError, log.insert) # No build
-
-        step = BuildStep(self.env, build=1)
-        self.assertRaises(AssertionError, log.insert) # No step
-
-    def test_delete(self):
-        db = self.env.get_db_cnx()
-        cursor = db.cursor()
-        cursor.execute("INSERT INTO bitten_log (build,step,generator) "
-                       "VALUES (%s,%s,%s)", (1, 'test', 'distutils'))
-        id = db.get_last_id(cursor, 'bitten_log')
-        cursor.executemany("INSERT INTO bitten_log_message "
-                           "VALUES (%s,%s,%s,%s)",
-                           [(id, 1, BuildLog.INFO, 'running tests'),
-                            (id, 2, BuildLog.ERROR, 'tests failed')])
-
-        log = BuildLog.fetch(self.env, id=id, db=db)
-        self.assertEqual(True, log.exists)
-        log.delete()
-        self.assertEqual(False, log.exists)
-
-        cursor.execute("SELECT * FROM bitten_log WHERE id=%s", (id,))
-        self.assertEqual(True, not cursor.fetchall())
-        cursor.execute("SELECT * FROM bitten_log_message WHERE log=%s", (id,))
-        self.assertEqual(True, not cursor.fetchall())
-
-    def test_delete_new(self):
-        log = BuildLog(self.env, build=1, step='test', generator='foo')
-        self.assertRaises(AssertionError, log.delete)
-
-    def test_fetch(self):
-        db = self.env.get_db_cnx()
-        cursor = db.cursor()
-        cursor.execute("INSERT INTO bitten_log (build,step,generator) "
-                       "VALUES (%s,%s,%s)", (1, 'test', 'distutils'))
-        id = db.get_last_id(cursor, 'bitten_log')
-        cursor.executemany("INSERT INTO bitten_log_message "
-                           "VALUES (%s,%s,%s,%s)",
-                           [(id, 1, BuildLog.INFO, 'running tests'),
-                            (id, 2, BuildLog.ERROR, 'tests failed')])
-
-        log = BuildLog.fetch(self.env, id=id, db=db)
-        self.assertEqual(True, log.exists)
-        self.assertEqual(id, log.id)
-        self.assertEqual(1, log.build)
-        self.assertEqual('test', log.step)
-        self.assertEqual('distutils', log.generator)
-        self.assertEqual((BuildLog.INFO, 'running tests'), log.messages[0])
-        self.assertEqual((BuildLog.ERROR, 'tests failed'), log.messages[1])
-
-    def test_select(self):
-        db = self.env.get_db_cnx()
-        cursor = db.cursor()
-        cursor.execute("INSERT INTO bitten_log (build,step,generator) "
-                       "VALUES (%s,%s,%s)", (1, 'test', 'distutils'))
-        id = db.get_last_id(cursor, 'bitten_log')
-        cursor.executemany("INSERT INTO bitten_log_message "
-                           "VALUES (%s,%s,%s,%s)",
-                           [(id, 1, BuildLog.INFO, 'running tests'),
-                            (id, 2, BuildLog.ERROR, 'tests failed')])
-
-        logs = BuildLog.select(self.env, build=1, step='test', db=db)
-        log = logs.next()
-        self.assertEqual(True, log.exists)
-        self.assertEqual(id, log.id)
-        self.assertEqual(1, log.build)
-        self.assertEqual('test', log.step)
-        self.assertEqual('distutils', log.generator)
-        self.assertEqual((BuildLog.INFO, 'running tests'), log.messages[0])
-        self.assertEqual((BuildLog.ERROR, 'tests failed'), log.messages[1])
-        self.assertRaises(StopIteration, logs.next)
-
-
-class ReportTestCase(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 Report._schema:
-            for stmt in connector.to_sql(table):
-                cursor.execute(stmt)
-        db.commit()
-
-    def test_delete(self):
-        db = self.env.get_db_cnx()
-        cursor = db.cursor()
-        cursor.execute("INSERT INTO bitten_report "
-                       "(build,step,category,generator) VALUES (%s,%s,%s,%s)",
-                       (1, 'test', 'test', 'unittest'))
-        report_id = db.get_last_id(cursor, 'bitten_report')
-        cursor.executemany("INSERT INTO bitten_report_item "
-                           "(report,item,name,value) VALUES (%s,%s,%s,%s)",
-                           [(report_id, 0, 'file', 'tests/foo.c'),
-                            (report_id, 0, 'result', 'failure'),
-                            (report_id, 1, 'file', 'tests/bar.c'),
-                            (report_id, 1, 'result', 'success')])
-
-        report = Report.fetch(self.env, report_id, db=db)
-        report.delete(db=db)
-        self.assertEqual(False, report.exists)
-        report = Report.fetch(self.env, report_id, db=db)
-        self.assertEqual(None, report)
-
-    def test_insert(self):
-        report = Report(self.env, build=1, step='test', category='test',
-                        generator='unittest')
-        report.items = [
-            {'file': 'tests/foo.c', 'status': 'failure'},
-            {'file': 'tests/bar.c', 'status': 'success'}
-        ]
-        report.insert()
-
-        db = self.env.get_db_cnx()
-        cursor = db.cursor()
-        cursor.execute("SELECT build,step,category,generator "
-                       "FROM bitten_report WHERE id=%s", (report.id,))
-        self.assertEqual((1, 'test', 'test', 'unittest'),
-                         cursor.fetchone())
-        cursor.execute("SELECT item,name,value FROM bitten_report_item "
-                       "WHERE report=%s ORDER BY item", (report.id,))
-        items = []
-        prev_item = None
-        for item, name, value in cursor:
-            if item != prev_item:
-                items.append({name: value})
-                prev_item = item
-            else:
-                items[-1][name] = value
-        self.assertEquals(2, len(items))
-        seen_foo, seen_bar = False, False
-        for item in items:
-            if item['file'] == 'tests/foo.c':
-                self.assertEqual('failure', item['status'])
-                seen_foo = True
-            if item['file'] == 'tests/bar.c':
-                self.assertEqual('success', item['status'])
-                seen_bar = True
-        self.assertEquals((True, True), (seen_foo, seen_bar))
-
-    def test_insert_dupe(self):
-        report = Report(self.env, build=1, step='test', category='test',
-                        generator='unittest')
-        report.insert()
-
-        report = Report(self.env, build=1, step='test', category='test',
-                        generator='unittest')
-        self.assertRaises(AssertionError, report.insert)
-
-    def test_insert_empty_items(self):
-        report = Report(self.env, build=1, step='test', category='test',
-                        generator='unittest')
-        report.items = [{}, {}]
-        report.insert()
-
-        db = self.env.get_db_cnx()
-        cursor = db.cursor()
-        cursor.execute("SELECT build,step,category,generator "
-                       "FROM bitten_report WHERE id=%s", (report.id,))
-        self.assertEqual((1, 'test', 'test', 'unittest'),
-                         cursor.fetchone())
-        cursor.execute("SELECT COUNT(*) FROM bitten_report_item "
-                       "WHERE report=%s", (report.id,))
-        self.assertEqual(0, cursor.fetchone()[0])
-
-    def test_fetch(self):
-        db = self.env.get_db_cnx()
-        cursor = db.cursor()
-        cursor.execute("INSERT INTO bitten_report "
-                       "(build,step,category,generator) VALUES (%s,%s,%s,%s)",
-                       (1, 'test', 'test', 'unittest'))
-        report_id = db.get_last_id(cursor, 'bitten_report')
-        cursor.executemany("INSERT INTO bitten_report_item "
-                           "(report,item,name,value) VALUES (%s,%s,%s,%s)",
-                           [(report_id, 0, 'file', 'tests/foo.c'),
-                            (report_id, 0, 'result', 'failure'),
-                            (report_id, 1, 'file', 'tests/bar.c'),
-                            (report_id, 1, 'result', 'success')])
-
-        report = Report.fetch(self.env, report_id)
-        self.assertEquals(report_id, report.id)
-        self.assertEquals('test', report.step)
-        self.assertEquals('test', report.category)
-        self.assertEquals('unittest', report.generator)
-        self.assertEquals(2, len(report.items))
-        assert {'file': 'tests/foo.c', 'result': 'failure'} in report.items
-        assert {'file': 'tests/bar.c', 'result': 'success'} in report.items
-
-    def test_select(self):
-        db = self.env.get_db_cnx()
-        cursor = db.cursor()
-        cursor.execute("INSERT INTO bitten_report "
-                       "(build,step,category,generator) VALUES (%s,%s,%s,%s)",
-                       (1, 'test', 'test', 'unittest'))
-        report1_id = db.get_last_id(cursor, 'bitten_report')
-        cursor.execute("INSERT INTO bitten_report "
-                       "(build,step,category,generator) VALUES (%s,%s,%s,%s)",
-                       (1, 'test', 'coverage', 'trace'))
-        report2_id = db.get_last_id(cursor, 'bitten_report')
-        cursor.executemany("INSERT INTO bitten_report_item "
-                           "(report,item,name,value) VALUES (%s,%s,%s,%s)",
-                           [(report1_id, 0, 'file', 'tests/foo.c'),
-                            (report1_id, 0, 'result', 'failure'),
-                            (report1_id, 1, 'file', 'tests/bar.c'),
-                            (report1_id, 1, 'result', 'success'),
-                            (report2_id, 0, 'file', 'tests/foo.c'),
-                            (report2_id, 0, 'loc', '12'),
-                            (report2_id, 0, 'cov', '50'),
-                            (report2_id, 1, 'file', 'tests/bar.c'),
-                            (report2_id, 1, 'loc', '20'),
-                            (report2_id, 1, 'cov', '25')])
-
-        reports = Report.select(self.env, build=1, step='test')
-        for idx, report in enumerate(reports):
-            if report.id == report1_id:
-                self.assertEquals('test', report.step)
-                self.assertEquals('test', report.category)
-                self.assertEquals('unittest', report.generator)
-                self.assertEquals(2, len(report.items))
-                assert {'file': 'tests/foo.c', 'result': 'failure'} \
-                       in report.items
-                assert {'file': 'tests/bar.c', 'result': 'success'} \
-                       in report.items
-            elif report.id == report1_id:
-                self.assertEquals('test', report.step)
-                self.assertEquals('coverage', report.category)
-                self.assertEquals('trace', report.generator)
-                self.assertEquals(2, len(report.items))
-                assert {'file': 'tests/foo.c', 'loc': '12', 'cov': '50'} \
-                       in report.items
-                assert {'file': 'tests/bar.c', 'loc': '20', 'cov': '25'} \
-                       in report.items
-        self.assertEqual(1, idx)
-
-
-def suite():
-    suite = unittest.TestSuite()
-    suite.addTest(unittest.makeSuite(BuildConfigTestCase, 'test'))
-    suite.addTest(unittest.makeSuite(TargetPlatformTestCase, 'test'))
-    suite.addTest(unittest.makeSuite(BuildTestCase, 'test'))
-    suite.addTest(unittest.makeSuite(BuildStepTestCase, 'test'))
-    suite.addTest(unittest.makeSuite(BuildLogTestCase, 'test'))
-    suite.addTest(unittest.makeSuite(ReportTestCase, 'test'))
-    return suite
-
-if __name__ == '__main__':
-    unittest.main(defaultTest='suite')
deleted file mode 100644
--- a/trac-0.11/bitten/tests/queue.py
+++ /dev/null
@@ -1,358 +0,0 @@
-# -*- 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 os
-import shutil
-import tempfile
-import time
-import unittest
-
-from trac.db import DatabaseManager
-from trac.test import EnvironmentStub, Mock
-from bitten.model import BuildConfig, TargetPlatform, Build, schema
-from bitten.queue import BuildQueue, collect_changes
-
-
-class CollectChangesTestCase(unittest.TestCase):
-    """
-    Unit tests for the `bitten.queue.collect_changes` function.
-    """
-
-    def setUp(self):
-        self.env = EnvironmentStub()
-        self.env.path = tempfile.mkdtemp()
-
-        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)
-
-        self.config = BuildConfig(self.env, name='test', path='somepath')
-        self.config.insert(db=db)
-        self.platform = TargetPlatform(self.env, config='test', name='Foo')
-        self.platform.insert(db=db)
-        db.commit()
-
-    def tearDown(self):
-        shutil.rmtree(self.env.path)
-
-    def test_stop_on_copy(self):
-        self.env.get_repository = lambda authname=None: Mock(
-            get_node=lambda path, rev=None: Mock(
-                get_history=lambda: [('otherpath', 123, 'copy')]
-            ),
-            normalize_path=lambda path: path
-        )
-
-        retval = list(collect_changes(self.env.get_repository(), self.config))
-        self.assertEqual(0, len(retval))
-
-    def test_stop_on_minrev(self):
-        self.env.get_repository = lambda authname=None: Mock(
-            get_node=lambda path, rev=None: Mock(
-                get_entries=lambda: [Mock(), Mock()],
-                get_history=lambda: [('somepath', 123, 'edit'),
-                                     ('somepath', 121, 'edit'),
-                                     ('somepath', 120, 'edit')]
-            ),
-            normalize_path=lambda path: path,
-            rev_older_than=lambda rev1, rev2: rev1 < rev2
-        )
-
-        self.config.min_rev = 123
-        self.config.update()
-
-        retval = list(collect_changes(self.env.get_repository(), self.config))
-        self.assertEqual(1, len(retval))
-        self.assertEqual(123, retval[0][1])
-
-    def test_skip_until_maxrev(self):
-        self.env.get_repository = lambda authname=None: Mock(
-            get_node=lambda path, rev=None: Mock(
-                get_entries=lambda: [Mock(), Mock()],
-                get_history=lambda: [('somepath', 123, 'edit'),
-                                     ('somepath', 121, 'edit'),
-                                     ('somepath', 120, 'edit')]
-            ),
-            normalize_path=lambda path: path,
-            rev_older_than=lambda rev1, rev2: rev1 < rev2
-        )
-
-        self.config.max_rev=121
-        self.config.update()
-
-        retval = list(collect_changes(self.env.get_repository(), self.config))
-        self.assertEqual(2, len(retval))
-        self.assertEqual(121, retval[0][1])
-        self.assertEqual(120, retval[1][1])
-
-    def test_skip_empty_dir(self):
-        def _mock_get_node(path, rev=None):
-            if rev and rev == 121:
-                return Mock(
-                    get_entries=lambda: []
-                )
-            else:
-                return Mock(
-                    get_entries=lambda: [Mock(), Mock()],
-                    get_history=lambda: [('somepath', 123, 'edit'),
-                                         ('somepath', 121, 'edit'),
-                                         ('somepath', 120, 'edit')]
-                )
-
-        self.env.get_repository = lambda authname=None: Mock(
-            get_node=_mock_get_node,
-            normalize_path=lambda path: path,
-            rev_older_than=lambda rev1, rev2: rev1 < rev2
-        )
-
-        retval = list(collect_changes(self.env.get_repository(), self.config))
-        self.assertEqual(2, len(retval))
-        self.assertEqual(123, retval[0][1])
-        self.assertEqual(120, retval[1][1])
-
-
-class BuildQueueTestCase(unittest.TestCase):
-
-    def setUp(self):
-        self.env = EnvironmentStub()
-        self.env.path = tempfile.mkdtemp()
-        os.mkdir(os.path.join(self.env.path, 'snapshots'))
-
-        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)
-        db.commit()
-
-        # Hook up a dummy repository
-        self.repos = Mock()
-        self.env.get_repository = lambda authname=None: self.repos
-
-    def tearDown(self):
-        shutil.rmtree(self.env.path)
-
-    def test_get_build_for_slave(self):
-        """
-        Make sure that a pending build of an activated configuration is
-        scheduled for a slave that matches the target platform.
-        """
-        BuildConfig(self.env, 'test', active=True).insert()
-        platform = TargetPlatform(self.env, config='test', name='Foo')
-        platform.insert()
-        build = Build(self.env, config='test', platform=platform.id, rev=123,
-                      rev_time=42, status=Build.PENDING)
-        build.insert()
-        build_id = build.id
-
-        queue = BuildQueue(self.env)
-        build = queue.get_build_for_slave('foobar', {})
-        self.assertEqual(build_id, build.id)
-
-    def test_next_pending_build_no_matching_slave(self):
-        """
-        Make sure that builds for which there is no slave matching the target
-        platform are not scheduled.
-        """
-        BuildConfig(self.env, 'test', active=True).insert()
-        build = Build(self.env, config='test', platform=1, rev=123, rev_time=42,
-                      status=Build.PENDING)
-        build.insert()
-        build_id = build.id
-
-        queue = BuildQueue(self.env)
-        build = queue.get_build_for_slave('foobar', {})
-        self.assertEqual(None, build)
-
-    def test_next_pending_build_inactive_config(self):
-        """
-        Make sure that builds for a deactived build config are not scheduled.
-        """
-        BuildConfig(self.env, 'test').insert()
-        platform = TargetPlatform(self.env, config='test', name='Foo')
-        platform.insert()
-        build = Build(self.env, config='test', platform=platform.id, rev=123,
-                      rev_time=42, status=Build.PENDING)
-        build.insert()
-
-        queue = BuildQueue(self.env)
-        build = queue.get_build_for_slave('foobar', {})
-        self.assertEqual(None, build)
-
-    def test_populate_not_build_all(self):
-        self.env.get_repository = lambda authname=None: Mock(
-            get_changeset=lambda rev: Mock(date=rev * 1000),
-            get_node=lambda path, rev=None: Mock(
-                get_entries=lambda: [Mock(), Mock()],
-                get_history=lambda: [('somepath', 123, 'edit'),
-                                     ('somepath', 121, 'edit'),
-                                     ('somepath', 120, 'edit')]
-            ),
-            normalize_path=lambda path: path,
-            rev_older_than=lambda rev1, rev2: rev1 < rev2
-        )
-        BuildConfig(self.env, 'test', path='somepath', active=True).insert()
-        platform1 = TargetPlatform(self.env, config='test', name='P1')
-        platform1.insert()
-        platform2 = TargetPlatform(self.env, config='test', name='P2')
-        platform2.insert()
-
-        queue = BuildQueue(self.env)
-        queue.populate()
-        queue.populate()
-        queue.populate()
-
-        builds = list(Build.select(self.env, config='test'))
-        builds.sort(lambda a, b: cmp(a.platform, b.platform))
-        self.assertEqual(2, len(builds))
-        self.assertEqual(platform1.id, builds[0].platform)
-        self.assertEqual('123', builds[0].rev)
-        self.assertEqual(platform2.id, builds[1].platform)
-        self.assertEqual('123', builds[1].rev)
-
-    def test_populate_build_all(self):
-        self.env.get_repository = lambda authname=None: Mock(
-            get_changeset=lambda rev: Mock(date=rev * 1000),
-            get_node=lambda path, rev=None: Mock(
-                get_entries=lambda: [Mock(), Mock()],
-                get_history=lambda: [('somepath', 123, 'edit'),
-                                     ('somepath', 121, 'edit'),
-                                     ('somepath', 120, 'edit')]
-            ),
-            normalize_path=lambda path: path,
-            rev_older_than=lambda rev1, rev2: rev1 < rev2
-        )
-        BuildConfig(self.env, 'test', path='somepath', active=True).insert()
-        platform1 = TargetPlatform(self.env, config='test', name='P1')
-        platform1.insert()
-        platform2 = TargetPlatform(self.env, config='test', name='P2')
-        platform2.insert()
-
-        queue = BuildQueue(self.env, build_all=True)
-        queue.populate()
-        queue.populate()
-        queue.populate()
-
-        builds = list(Build.select(self.env, config='test'))
-        builds.sort(lambda a, b: cmp(a.platform, b.platform))
-        self.assertEqual(6, len(builds))
-        self.assertEqual(platform1.id, builds[0].platform)
-        self.assertEqual('123', builds[0].rev)
-        self.assertEqual(platform1.id, builds[1].platform)
-        self.assertEqual('121', builds[1].rev)
-        self.assertEqual(platform1.id, builds[2].platform)
-        self.assertEqual('120', builds[2].rev)
-        self.assertEqual(platform2.id, builds[3].platform)
-        self.assertEqual('123', builds[3].rev)
-        self.assertEqual(platform2.id, builds[4].platform)
-        self.assertEqual('121', builds[4].rev)
-        self.assertEqual(platform2.id, builds[5].platform)
-        self.assertEqual('120', builds[5].rev)
-
-    def test_reset_orphaned_builds(self):
-        BuildConfig(self.env, 'test').insert()
-        platform = TargetPlatform(self.env, config='test', name='Foo')
-        platform.insert()
-        build1 = Build(self.env, config='test', platform=platform.id, rev=123,
-                      rev_time=42, status=Build.IN_PROGRESS, slave='heinz',
-                      started=time.time() - 600) # Started ten minutes ago
-        build1.insert()
-
-        build2 = Build(self.env, config='test', platform=platform.id, rev=124,
-                       rev_time=42, status=Build.IN_PROGRESS, slave='heinz',
-                       started=time.time() - 60) # Started a minute ago
-        build2.insert()
-
-        queue = BuildQueue(self.env, timeout=300) # 5 minutes timeout
-        build = queue.reset_orphaned_builds()
-        self.assertEqual(Build.PENDING, Build.fetch(self.env, build1.id).status)
-        self.assertEqual(Build.IN_PROGRESS,
-                         Build.fetch(self.env, build2.id).status)
-
-    def test_match_slave_match(self):
-        BuildConfig(self.env, 'test', active=True).insert()
-        platform = TargetPlatform(self.env, config='test', name="Unix")
-        platform.rules.append(('family', 'posix'))
-        platform.insert()
-        platform_id = platform.id
-
-        queue = BuildQueue(self.env)
-        platforms = queue.match_slave('foo', {'family': 'posix'})
-        self.assertEqual(1, len(platforms))
-        self.assertEqual(platform_id, platforms[0].id)
-
-    def test_register_slave_match_simple_fail(self):
-        BuildConfig(self.env, 'test', active=True).insert()
-        platform = TargetPlatform(self.env, config='test', name="Unix")
-        platform.rules.append(('family', 'posix'))
-        platform.insert()
-
-        queue = BuildQueue(self.env)
-        platforms = queue.match_slave('foo', {'family': 'nt'})
-        self.assertEqual([], platforms)
-
-    def test_register_slave_match_regexp(self):
-        BuildConfig(self.env, 'test', active=True).insert()
-        platform = TargetPlatform(self.env, config='test', name="Unix")
-        platform.rules.append(('version', '8\.\d\.\d'))
-        platform.insert()
-        platform_id = platform.id
-
-        queue = BuildQueue(self.env)
-        platforms = queue.match_slave('foo', {'version': '8.2.0'})
-        self.assertEqual(1, len(platforms))
-        self.assertEqual(platform_id, platforms[0].id)
-
-    def test_register_slave_match_regexp_multi(self):
-        BuildConfig(self.env, 'test', active=True).insert()
-        platform = TargetPlatform(self.env, config='test', name="Unix")
-        platform.rules.append(('os', '^Linux'))
-        platform.rules.append(('processor', '^[xi]\d?86$'))
-        platform.insert()
-        platform_id = platform.id
-
-        queue = BuildQueue(self.env)
-        platforms = queue.match_slave('foo', {'os': 'Linux', 'processor': 'i686'})
-        self.assertEqual(1, len(platforms))
-        self.assertEqual(platform_id, platforms[0].id)
-
-    def test_register_slave_match_regexp_fail(self):
-        BuildConfig(self.env, 'test', active=True).insert()
-        platform = TargetPlatform(self.env, config='test', name="Unix")
-        platform.rules.append(('version', '8\.\d\.\d'))
-        platform.insert()
-
-        queue = BuildQueue(self.env)
-        platforms = queue.match_slave('foo', {'version': '7.8.1'})
-        self.assertEqual([], platforms)
-
-    def test_register_slave_match_regexp_invalid(self):
-        BuildConfig(self.env, 'test', active=True).insert()
-        platform = TargetPlatform(self.env, config='test', name="Unix")
-        platform.rules.append(('version', '8(\.\d'))
-        platform.insert()
-
-        queue = BuildQueue(self.env)
-        platforms = queue.match_slave('foo', {'version': '7.8.1'})
-        self.assertEqual([], platforms)
-
-
-def suite():
-    suite = unittest.TestSuite()
-    suite.addTest(unittest.makeSuite(CollectChangesTestCase, 'test'))
-    suite.addTest(unittest.makeSuite(BuildQueueTestCase, 'test'))
-    return suite
-
-if __name__ == '__main__':
-    unittest.main(defaultTest='suite')
deleted file mode 100644
--- a/trac-0.11/bitten/tests/recipe.py
+++ /dev/null
@@ -1,108 +0,0 @@
-# -*- 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 os
-import shutil
-import tempfile
-import unittest
-
-from bitten.recipe import Recipe, InvalidRecipeError
-from bitten.util import xmlio
-
-
-class RecipeTestCase(unittest.TestCase):
-
-    def setUp(self):
-        self.basedir = os.path.realpath(tempfile.mkdtemp())
-
-    def tearDown(self):
-        shutil.rmtree(self.basedir)
-
-    def test_empty_recipe(self):
-        xml = xmlio.parse('<build/>')
-        recipe = Recipe(xml, basedir=self.basedir)
-        self.assertEqual(self.basedir, recipe.ctxt.basedir)
-        steps = list(recipe)
-        self.assertEqual(0, len(steps))
-
-    def test_empty_step(self):
-        xml = xmlio.parse('<build>'
-                          ' <step id="foo" description="Bar"></step>'
-                          '</build>')
-        recipe = Recipe(xml, basedir=self.basedir)
-        steps = list(recipe)
-        self.assertEqual(1, len(steps))
-        self.assertEqual('foo', steps[0].id)
-        self.assertEqual('Bar', steps[0].description)
-        self.assertEqual('fail', steps[0].onerror)
-
-    def test_validate_bad_root(self):
-        xml = xmlio.parse('<foo></foo>')
-        recipe = Recipe(xml, basedir=self.basedir)
-        self.assertRaises(InvalidRecipeError, recipe.validate)
-
-    def test_validate_no_steps(self):
-        xml = xmlio.parse('<build></build>')
-        recipe = Recipe(xml, basedir=self.basedir)
-        self.assertRaises(InvalidRecipeError, recipe.validate)
-
-    def test_validate_child_not_step(self):
-        xml = xmlio.parse('<build><foo/></build>')
-        recipe = Recipe(xml, basedir=self.basedir)
-        self.assertRaises(InvalidRecipeError, recipe.validate)
-
-    def test_validate_child_not_step(self):
-        xml = xmlio.parse('<build><foo/></build>')
-        recipe = Recipe(xml, basedir=self.basedir)
-        self.assertRaises(InvalidRecipeError, recipe.validate)
-
-    def test_validate_step_without_id(self):
-        xml = xmlio.parse('<build><step><cmd/></step></build>')
-        recipe = Recipe(xml, basedir=self.basedir)
-        self.assertRaises(InvalidRecipeError, recipe.validate)
-
-    def test_validate_step_with_empty_id(self):
-        xml = xmlio.parse('<build><step id=""><cmd/></step></build>')
-        recipe = Recipe(xml, basedir=self.basedir)
-        self.assertRaises(InvalidRecipeError, recipe.validate)
-
-    def test_validate_step_without_commands(self):
-        xml = xmlio.parse('<build><step id="test"/></build>')
-        recipe = Recipe(xml, basedir=self.basedir)
-        self.assertRaises(InvalidRecipeError, recipe.validate)
-
-    def test_validate_step_with_command_children(self):
-        xml = xmlio.parse('<build><step id="test">'
-                          '<somecmd><child1/><child2/></somecmd>'
-                          '</step></build>')
-        recipe = Recipe(xml, basedir=self.basedir)
-        self.assertRaises(InvalidRecipeError, recipe.validate)
-
-    def test_validate_step_with_duplicate_id(self):
-        xml = xmlio.parse('<build>'
-                          '<step id="test"><somecmd></somecmd></step>'
-                          '<step id="test"><othercmd></othercmd></step>'
-                          '</build>')
-        recipe = Recipe(xml, basedir=self.basedir)
-        self.assertRaises(InvalidRecipeError, recipe.validate)
-
-    def test_validate_successful(self):
-        xml = xmlio.parse('<build>'
-                          '<step id="foo"><somecmd></somecmd></step>'
-                          '<step id="bar"><othercmd></othercmd></step>'
-                          '</build>')
-        recipe = Recipe(xml, basedir=self.basedir)
-        recipe.validate()
-
-def suite():
-    return unittest.makeSuite(RecipeTestCase, 'test')
-
-if __name__ == '__main__':
-    unittest.main(defaultTest='suite')
deleted file mode 100644
--- a/trac-0.11/bitten/tests/slave.py
+++ /dev/null
@@ -1,42 +0,0 @@
-# -*- 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 os
-import shutil
-import tempfile
-import unittest
-
-from trac.test import Mock
-from bitten.slave import BuildSlave
-
-
-class BuildSlaveTestCase(unittest.TestCase):
-
-    def setUp(self):
-        self.work_dir = tempfile.mkdtemp(prefix='bitten_test')
-        self.slave = BuildSlave(None, work_dir=self.work_dir)
-
-    def tearDown(self):
-        shutil.rmtree(self.work_dir)
-
-    def _create_file(self, *path):
-        filename = os.path.join(self.work_dir, *path)
-        fd = file(filename, 'w')
-        fd.close()
-        return filename
-
-
-def suite():
-    suite = unittest.TestSuite()
-    suite.addTest(unittest.makeSuite(BuildSlaveTestCase, 'test'))
-    return suite
-
-if __name__ == '__main__':
-    unittest.main(defaultTest='suite')
deleted file mode 100644
--- a/trac-0.11/bitten/tests/web_ui.py
+++ /dev/null
@@ -1,235 +0,0 @@
-# -*- 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 shutil
-import tempfile
-import unittest
-
-from trac.core import TracError
-from trac.db import DatabaseManager
-from trac.perm import PermissionCache, PermissionSystem
-from trac.test import EnvironmentStub, Mock
-from trac.util.html import Markup
-from trac.web.href import Href
-from bitten.main import BuildSystem
-from bitten.model import Build, BuildConfig, BuildStep, TargetPlatform, schema
-from bitten.web_ui import BuildConfigController, SourceFileLinkFormatter
-
-
-class BuildConfigControllerTestCase(unittest.TestCase):
-
-    def setUp(self):
-        self.env = EnvironmentStub(enable=['trac.*', 'bitten.*'])
-        self.env.path = tempfile.mkdtemp()
-
-        # Create tables
-        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)
-
-        # Set up permissions
-        self.env.config.set('trac', 'permission_store',
-                            'DefaultPermissionStore')
-
-        # Hook up a dummy repository
-        self.repos = Mock(
-            get_node=lambda path, rev=None: Mock(get_history=lambda: [],
-                                                 isdir=True),
-            normalize_path=lambda path: path,
-            sync=lambda: None
-        )
-        self.env.get_repository = lambda authname=None: self.repos
-
-    def tearDown(self):
-        shutil.rmtree(self.env.path)
-
-    def test_overview(self):
-        PermissionSystem(self.env).grant_permission('joe', 'BUILD_VIEW')
-        req = Mock(method='GET', base_path='', cgi_location='',
-                   path_info='/build', href=Href('/trac'), args={}, chrome={},
-                   perm=PermissionCache(self.env, 'joe'))
-
-        module = BuildConfigController(self.env)
-        assert module.match_request(req)
-        _, data, _ = module.process_request(req)
-
-        self.assertEqual('overview', data['page_mode'])
-
-    def test_view_config(self):
-        config = BuildConfig(self.env, name='test', path='trunk')
-        config.insert()
-        platform = TargetPlatform(self.env, config='test', name='any')
-        platform.insert()
-
-        PermissionSystem(self.env).grant_permission('joe', 'BUILD_VIEW')
-        req = Mock(method='GET', base_path='', cgi_location='',
-                   path_info='/build/test', href=Href('/trac'), args={},
-                   chrome={}, authname='joe',
-                   perm=PermissionCache(self.env, 'joe'))
-
-        root = Mock(get_entries=lambda: ['foo'],
-                    get_history=lambda: [('trunk', rev, 'edit') for rev in
-                                          range(123, 111, -1)])
-        self.repos = Mock(get_node=lambda path, rev=None: root,
-                          sync=lambda: None, normalize_path=lambda path: path)
-
-        module = BuildConfigController(self.env)
-        assert module.match_request(req)
-        _, data, _ = module.process_request(req)
-
-        self.assertEqual('view_config', data['page_mode'])
-        assert not 'next' in req.chrome['links']
-
-    def test_view_config_paging(self):
-        config = BuildConfig(self.env, name='test', path='trunk')
-        config.insert()
-        platform = TargetPlatform(self.env, config='test', name='any')
-        platform.insert()
-
-        PermissionSystem(self.env).grant_permission('joe', 'BUILD_VIEW')
-        req = Mock(method='GET', base_path='', cgi_location='',
-                   path_info='/build/test', href=Href('/trac'), args={},
-                   chrome={}, authname='joe',
-                   perm=PermissionCache(self.env, 'joe'))
-
-        root = Mock(get_entries=lambda: ['foo'],
-                    get_history=lambda: [('trunk', rev, 'edit') for rev in
-                                          range(123, 110, -1)])
-        self.repos = Mock(get_node=lambda path, rev=None: root,
-                          sync=lambda: None, normalize_path=lambda path: path)
-
-        module = BuildConfigController(self.env)
-        assert module.match_request(req)
-        _, data, _ = module.process_request(req)
-
-        if req.chrome:
-            self.assertEqual('/trac/build/test?page=2',
-                             req.chrome['links']['next'][0]['href'])
-
-
-class SourceFileLinkFormatterTestCase(unittest.TestCase):
-
-    def setUp(self):
-        self.env = EnvironmentStub(enable=['trac.*', 'bitten.*'])
-
-        # Create tables
-        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)
-
-        # Hook up a dummy repository
-        self.repos = Mock(
-            get_node=lambda path, rev=None: Mock(get_history=lambda: [],
-                                                 isdir=True),
-            normalize_path=lambda path: path,
-            sync=lambda: None
-        )
-        self.env.get_repository = lambda authname=None: self.repos
-
-    def tearDown(self):
-        pass
-
-    def test_format_simple_link_in_repos(self):
-        BuildConfig(self.env, name='test', path='trunk').insert()
-        build = Build(self.env, config='test', platform=1, rev=123, rev_time=42,
-                      status=Build.SUCCESS, slave='hal')
-        build.insert()
-        step = BuildStep(self.env, build=build.id, name='foo',
-                         status=BuildStep.SUCCESS)
-        step.insert()
-
-        self.repos.get_node = lambda path, rev: (path, rev)
-
-        req = Mock(method='GET', href=Href('/trac'), authname='hal')
-        comp = SourceFileLinkFormatter(self.env)
-        formatter = comp.get_formatter(req, build)
-
-        output = formatter(step, None, None, u'error in foo/bar.c: bad')
-        self.assertEqual(Markup, type(output))
-        self.assertEqual('error in <a href="/trac/browser/trunk/foo/bar.c">'
-                         'foo/bar.c</a>: bad', output)
-
-    def test_format_simple_link_not_in_repos(self):
-        BuildConfig(self.env, name='test', path='trunk').insert()
-        build = Build(self.env, config='test', platform=1, rev=123, rev_time=42,
-                      status=Build.SUCCESS, slave='hal')
-        build.insert()
-        step = BuildStep(self.env, build=build.id, name='foo',
-                         status=BuildStep.SUCCESS)
-        step.insert()
-
-        def _raise():
-            raise TracError('No such node')
-        self.repos.get_node = lambda path, rev: _raise()
-
-        req = Mock(method='GET', href=Href('/trac'), authname='hal')
-        comp = SourceFileLinkFormatter(self.env)
-        formatter = comp.get_formatter(req, build)
-
-        output = formatter(step, None, None, u'error in foo/bar.c: bad')
-        self.assertEqual(Markup, type(output))
-        self.assertEqual('error in foo/bar.c: bad', output)
-
-    def test_format_link_in_repos_with_line(self):
-        BuildConfig(self.env, name='test', path='trunk').insert()
-        build = Build(self.env, config='test', platform=1, rev=123, rev_time=42,
-                      status=Build.SUCCESS, slave='hal')
-        build.insert()
-        step = BuildStep(self.env, build=build.id, name='foo',
-                         status=BuildStep.SUCCESS)
-        step.insert()
-
-        self.repos.get_node = lambda path, rev: (path, rev)
-
-        req = Mock(method='GET', href=Href('/trac'), authname='hal')
-        comp = SourceFileLinkFormatter(self.env)
-        formatter = comp.get_formatter(req, build)
-
-        output = formatter(step, None, None, u'error in foo/bar.c:123: bad')
-        self.assertEqual(Markup, type(output))
-        self.assertEqual('error in <a href="/trac/browser/trunk/foo/bar.c#L123">'
-                         'foo/bar.c:123</a>: bad', output)
-
-    def test_format_link_not_in_repos_with_line(self):
-        BuildConfig(self.env, name='test', path='trunk').insert()
-        build = Build(self.env, config='test', platform=1, rev=123, rev_time=42,
-                      status=Build.SUCCESS, slave='hal')
-        build.insert()
-        step = BuildStep(self.env, build=build.id, name='foo',
-                         status=BuildStep.SUCCESS)
-        step.insert()
-
-        def _raise():
-            raise TracError('No such node')
-        self.repos.get_node = lambda path, rev: _raise()
-
-        req = Mock(method='GET', href=Href('/trac'), authname='hal')
-        comp = SourceFileLinkFormatter(self.env)
-        formatter = comp.get_formatter(req, build)
-
-        output = formatter(step, None, None, u'error in foo/bar.c:123: bad')
-        self.assertEqual(Markup, type(output))
-        self.assertEqual('error in foo/bar.c:123: bad', output)
-
-
-def suite():
-    suite = unittest.TestSuite()
-    suite.addTest(unittest.makeSuite(BuildConfigControllerTestCase, 'test'))
-    suite.addTest(unittest.makeSuite(SourceFileLinkFormatterTestCase, 'test'))
-    return suite
-
-if __name__ == '__main__':
-    unittest.main(defaultTest='suite')
deleted file mode 100644
--- a/trac-0.11/bitten/upgrades.py
+++ /dev/null
@@ -1,291 +0,0 @@
-# -*- coding: utf-8 -*-
-#
-# Copyright (C) 2007 Edgewall Software
-# Copyright (C) 2005-2007 Christopher Lenz <cmlenz@gmx.de>
-# 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.
-
-"""Automated upgrades for the Bitten database tables, and other data stored
-in the Trac environment."""
-
-import os
-import sys
-
-from trac.db import DatabaseManager
-
-__docformat__ = 'restructuredtext en'
-
-def add_log_table(env, db):
-    """Add a table for storing the builds logs."""
-    from bitten.model import BuildLog, BuildStep
-    cursor = db.cursor()
-
-    connector, _ = DatabaseManager(env)._get_connector()
-    for table in BuildLog._schema:
-        for stmt in connector.to_sql(table):
-            cursor.execute(stmt)
-
-    cursor.execute("SELECT build,name,log FROM bitten_step "
-                   "WHERE log IS NOT NULL")
-    for build, step, log in cursor:
-        build_log = BuildLog(env, build, step)
-        build_log.messages = [(BuildLog.INFO, msg) for msg in log.splitlines()]
-        build_log.insert(db)
-
-    cursor.execute("CREATE TEMP TABLE old_step AS SELECT * FROM bitten_step")
-    cursor.execute("DROP TABLE bitten_step")
-    for table in BuildStep._schema:
-        for stmt in connector.to_sql(table):
-            cursor.execute(stmt)
-    cursor.execute("INSERT INTO bitten_step (build,name,description,status,"
-                   "started,stopped) SELECT build,name,description,status,"
-                   "started,stopped FROM old_step")
-
-def add_recipe_to_config(env, db):
-    """Add a column for storing the build recipe to the build configuration
-    table."""
-    from bitten.model import BuildConfig
-    cursor = db.cursor()
-
-    cursor.execute("CREATE TEMP TABLE old_config AS "
-                   "SELECT * FROM bitten_config")
-    cursor.execute("DROP TABLE bitten_config")
-
-    connector, _ = DatabaseManager(env)._get_connector()
-    for table in BuildConfig._schema:
-        for stmt in connector.to_sql(table):
-            cursor.execute(stmt)
-
-    cursor.execute("INSERT INTO bitten_config (name,path,active,recipe,min_rev,"
-                   "max_rev,label,description) SELECT name,path,0,'',NULL,"
-                   "NULL,label,description FROM old_config")
-
-def add_config_to_reports(env, db):
-    """Add the name of the build configuration as metadata to report documents
-    stored in the BDB XML database."""
-
-    from bitten.model import Build
-    try:
-        from bsddb3 import db as bdb
-        import dbxml
-    except ImportError:
-        return
-
-    dbfile = os.path.join(env.path, 'db', 'bitten.dbxml')
-    if not os.path.isfile(dbfile):
-        return
-
-    dbenv = bdb.DBEnv()
-    dbenv.open(os.path.dirname(dbfile),
-               bdb.DB_CREATE | bdb.DB_INIT_LOCK | bdb.DB_INIT_LOG |
-               bdb.DB_INIT_MPOOL | bdb.DB_INIT_TXN, 0)
-
-    mgr = dbxml.XmlManager(dbenv, 0)
-    xtn = mgr.createTransaction()
-    container = mgr.openContainer(dbfile, dbxml.DBXML_TRANSACTIONAL)
-    uc = mgr.createUpdateContext()
-
-    container.addIndex(xtn, '', 'config', 'node-metadata-equality-string', uc)
-
-    qc = mgr.createQueryContext()
-    for value in mgr.query(xtn, 'collection("%s")/report' % dbfile, qc):
-        doc = value.asDocument()
-        metaval = dbxml.XmlValue()
-        if doc.getMetaData('', 'build', metaval):
-            build_id = int(metaval.asNumber())
-            build = Build.fetch(env, id=build_id, db=db)
-            if build:
-                doc.setMetaData('', 'config', dbxml.XmlValue(build.config))
-                container.updateDocument(xtn, doc, uc)
-            else:
-                # an orphaned report, for whatever reason... just remove it
-                container.deleteDocument(xtn, doc, uc)
-
-    xtn.commit()
-    container.close()
-    dbenv.close(0)
-
-def add_order_to_log(env, db):
-    """Add order column to log table to make sure that build logs are displayed
-    in the order they were generated."""
-    from bitten.model import BuildLog
-    cursor = db.cursor()
-
-    cursor.execute("CREATE TEMP TABLE old_log AS "
-                   "SELECT * FROM bitten_log")
-    cursor.execute("DROP TABLE bitten_log")
-
-    connector, _ = DatabaseManager(env)._get_connector()
-    for stmt in connector.to_sql(BuildLog._schema[0]):
-        cursor.execute(stmt)
-
-    cursor.execute("INSERT INTO bitten_log (id,build,step,generator,orderno) "
-                   "SELECT id,build,step,type,0 FROM old_log")
-
-def add_report_tables(env, db):
-    """Add database tables for report storage."""
-    from bitten.model import Report
-    cursor = db.cursor()
-
-    connector, _ = DatabaseManager(env)._get_connector()
-    for table in Report._schema:
-        for stmt in connector.to_sql(table):
-            cursor.execute(stmt)
-
-def xmldb_to_db(env, db):
-    """Migrate report data from Berkeley DB XML to SQL database.
-    
-    Depending on the number of reports stored, this might take rather long.
-    After the upgrade is done, the bitten.dbxml file (and any BDB XML log files)
-    may be deleted. BDB XML is no longer used by Bitten.
-    """
-    from bitten.model import Report
-    from bitten.util import xmlio
-    try:
-        from bsddb3 import db as bdb
-        import dbxml
-    except ImportError:
-        return
-
-    dbfile = os.path.join(env.path, 'db', 'bitten.dbxml')
-    if not os.path.isfile(dbfile):
-        return
-
-    dbenv = bdb.DBEnv()
-    dbenv.open(os.path.dirname(dbfile),
-               bdb.DB_CREATE | bdb.DB_INIT_LOCK | bdb.DB_INIT_LOG |
-               bdb.DB_INIT_MPOOL | bdb.DB_INIT_TXN, 0)
-
-    mgr = dbxml.XmlManager(dbenv, 0)
-    xtn = mgr.createTransaction()
-    container = mgr.openContainer(dbfile, dbxml.DBXML_TRANSACTIONAL)
-
-    def get_pylint_items(xml):
-        for problems_elem in xml.children('problems'):
-            for problem_elem in problems_elem.children('problem'):
-                item = {'type': 'problem'}
-                item.update(problem_elem.attr)
-                yield item
-
-    def get_trace_items(xml):
-        for cov_elem in xml.children('coverage'):
-            item = {'type': 'coverage', 'name': cov_elem.attr['module'],
-                    'file': cov_elem.attr['file'],
-                    'percentage': cov_elem.attr['percentage']}
-            lines = 0
-            line_hits = []
-            for line_elem in cov_elem.children('line'):
-                lines += 1
-                line_hits.append(line_elem.attr['hits'])
-            item['lines'] = lines
-            item['line_hits'] = ' '.join(line_hits)
-            yield item
-
-    def get_unittest_items(xml):
-        for test_elem in xml.children('test'):
-            item = {'type': 'test'}
-            item.update(test_elem.attr)
-            for child_elem in test_elem.children():
-                item[child_elem.name] = child_elem.gettext()
-            yield item
-
-    qc = mgr.createQueryContext()
-    for value in mgr.query(xtn, 'collection("%s")/report' % dbfile, qc, 0):
-        doc = value.asDocument()
-        metaval = dbxml.XmlValue()
-        build, step = None, None
-        if doc.getMetaData('', 'build', metaval):
-            build = metaval.asNumber()
-        if doc.getMetaData('', 'step', metaval):
-            step = metaval.asString()
-
-        report_types = {'pylint':   ('lint', get_pylint_items),
-                        'trace':    ('coverage', get_trace_items),
-                        'unittest': ('test', get_unittest_items)}
-        xml = xmlio.parse(value.asString())
-        report_type = xml.attr['type']
-        category, get_items = report_types[report_type]
-        sys.stderr.write('.')
-        sys.stderr.flush()
-        report = Report(env, build, step, category=category,
-                        generator=report_type)
-        report.items = list(get_items(xml))
-        try:
-            report.insert(db=db)
-        except AssertionError:
-            # Duplicate report, skip
-            pass
-    sys.stderr.write('\n')
-    sys.stderr.flush()
-
-    xtn.abort()
-    container.close()
-    dbenv.close(0)
-
-def normalize_file_paths(env, db):
-    """Normalize the file separator in file names in reports."""
-    cursor = db.cursor()
-    cursor.execute("SELECT report,item,value FROM bitten_report_item "
-                   "WHERE name='file'")
-    rows = cursor.fetchall() or []
-    for report, item, value in rows:
-        if '\\' in value:
-            cursor.execute("UPDATE bitten_report_item SET value=%s "
-                           "WHERE report=%s AND item=%s AND name='file'",
-                           (value.replace('\\', '/'), report, item))
-
-def fixup_generators(env, db):
-    """Upgrade the identifiers for the recipe commands that generated log
-    messages and report data."""
-
-    mapping = {
-        'pipe': 'http://bitten.cmlenz.net/tools/sh#pipe',
-        'make': 'http://bitten.cmlenz.net/tools/c#make',
-        'distutils': 'http://bitten.cmlenz.net/tools/python#distutils',
-        'exec_': 'http://bitten.cmlenz.net/tools/python#exec' # Ambigious
-    }
-    cursor = db.cursor()
-    cursor.execute("SELECT id,generator FROM bitten_log "
-                   "WHERE generator IN (%s)"
-                   % ','.join([repr(key) for key in mapping.keys()]))
-    for log_id, generator in cursor:
-        cursor.execute("UPDATE bitten_log SET generator=%s "
-                       "WHERE id=%s", (mapping[generator], log_id))
-
-    mapping = {
-        'unittest': 'http://bitten.cmlenz.net/tools/python#unittest',
-        'trace': 'http://bitten.cmlenz.net/tools/python#trace',
-        'pylint': 'http://bitten.cmlenz.net/tools/python#pylint'
-    }
-    cursor.execute("SELECT id,generator FROM bitten_report "
-                   "WHERE generator IN (%s)"
-                   % ','.join([repr(key) for key in mapping.keys()]))
-    for report_id, generator in cursor:
-        cursor.execute("UPDATE bitten_report SET generator=%s "
-                       "WHERE id=%s", (mapping[generator], report_id))
-
-def add_error_table(env, db):
-    """Add the bitten_error table for recording step failure reasons."""
-    from trac.db import Table, Column
-
-    table = Table('bitten_error', key=('build', 'step', 'orderno'))[
-                Column('build', type='int'), Column('step'), Column('message'),
-                Column('orderno', type='int')
-            ]
-    cursor = db.cursor()
-
-    connector, _ = DatabaseManager(env)._get_connector()
-    for stmt in connector.to_sql(table):
-        cursor.execute(stmt)
-
-map = {
-    2: [add_log_table],
-    3: [add_recipe_to_config],
-    4: [add_config_to_reports],
-    5: [add_order_to_log, add_report_tables, xmldb_to_db],
-    6: [normalize_file_paths, fixup_generators],
-    7: [add_error_table]
-}
deleted file mode 100644
--- a/trac-0.11/bitten/util/__init__.py
+++ /dev/null
@@ -1,17 +0,0 @@
-# -*- 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.
-
-"""Generic utility functions and classes.
-
-Functionality in these modules have no dependencies on modules outside of this
-package, so that they could theoretically be used in other projects.
-"""
-
-__docformat__ = 'restructuredtext en'
deleted file mode 100644
--- a/trac-0.11/bitten/util/loc.py
+++ /dev/null
@@ -1,155 +0,0 @@
-# -*- coding: utf-8 -*-
-#
-# Copyright (C) 1998 Dinu C. Gherman <gherman@europemail.com>
-# Copyright (C) 2005-2007 Christopher Lenz <cmlenz@gmx.de>
-# Copyright (C) 2007 Edgewall Software
-# 
-# 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.
-# 
-# This module is based on the pycount.py script written by Dinu C.
-# Gherman, and is used here under the following license:
-# 
-#     Permission to use, copy, modify, and distribute this software
-#     and its documentation without fee and for any purpose, except
-#     direct commerial advantage, is hereby granted, provided that
-#     the above copyright notice appear in all copies and that both
-#     that copyright notice and this  permission notice appear in
-#     supporting documentation.
-
-"""Support for counting the lines of code in Python programs."""
-
-import re
-
-__all__ = ['BLANK', 'CODE', 'COMMENT', 'DOC', 'count']
-__docformat__ = 'restructuredtext en'
-
-# Reg. exps. to find the end of a triple quote, given that
-# we know we're in one; use the "match" method; .span()[1]
-# will be the index of the character following the final
-# quote.
-_squote3_finder = re.compile(
-    r"([^\']|"
-    r"\.|"
-    r"'[^\']|"
-    r"'\.|"
-    r"''[^\']|"
-    r"''\.)*'''")
-
-_dquote3_finder = re.compile(
-    r'([^\"]|'
-    r'\.|'
-    r'"[^\"]|'
-    r'"\.|'
-    r'""[^\"]|'
-    r'""\.)*"""')
-
-# Reg. exps. to find the leftmost one-quoted string; use the
-# "search" method; .span()[0] bounds the string found.
-_dquote1_finder = re.compile(r'"([^"]|\.)*"')
-_squote1_finder = re.compile(r"'([^']|\.)*'")
-
-# _is_comment matches pure comment line.
-_is_comment = re.compile(r"^[ \t]*#").match
-
-# _is_blank matches empty line.
-_is_blank = re.compile(r"^[ \t]*$").match
-
-# find leftmost splat or quote.
-_has_nightmare = re.compile(r"""[\"'#]""").search
-
-# _is_doc_candidate matches lines that start with a triple quote.
-_is_doc_candidate = re.compile(r"^[ \t]*('''|\"\"\")")
-
-BLANK, CODE, COMMENT, DOC  = 0, 1, 2, 3
-
-def count(source):
-    """Parse the given file-like object as Python source code.
-    
-    For every line in the code, this function yields a ``(lineno, type, line)``
-    tuple, where ``lineno`` is the line number (starting at 0), ``type`` is
-    one of `BLANK`, `CODE`, `COMMENT` or `DOC`, and ``line`` is the actual
-    content of the line.
-    
-    :param source: a file-like object containing Python code
-    """
-
-    quote3_finder = {'"': _dquote3_finder, "'": _squote3_finder}
-    quote1_finder = {'"': _dquote1_finder, "'": _squote1_finder }
-
-    in_doc = False
-    in_triple_quote = None
-
-    for lineno, line in enumerate(source):
-        classified = False
-
-        if in_triple_quote:
-            if in_doc:
-                yield lineno, DOC, line
-            else:
-                yield lineno, CODE, line
-            classified = True
-            m = in_triple_quote.match(line)
-            if m == None:
-                continue
-            # Get rid of everything through the end of the triple.
-            end = m.span()[1]
-            line = line[end:]
-            in_doc = in_triple_quote = False
-
-        if _is_blank(line):
-            if not classified:
-                yield lineno, BLANK, line
-            continue
-
-        if _is_comment(line):
-            if not classified:
-                yield lineno, COMMENT, line
-            continue
-
-        # Now we have a code line, a doc start line, or crap left
-        # over following the close of a multi-line triple quote; in
-        # (& only in) the last case, classified==1.
-        if not classified:
-            if _is_doc_candidate.match(line):
-                yield lineno, DOC, line
-                in_doc = True
-            else:
-                yield lineno, CODE, line
-
-        # The only reason to continue parsing is to make sure the
-        # start of a multi-line triple quote isn't missed.
-        while True:
-            m = _has_nightmare(line)
-            if not m:
-                break
-            else:
-                i = m.span()[0]
-
-            ch = line[i]    # splat or quote
-            if ch == '#':
-                # Chop off comment; and there are no quotes
-                # remaining because splat was leftmost.
-                break
-            # A quote is leftmost.
-            elif ch * 3 == line[i:i + 3]:
-                # at the start of a triple quote
-                in_triple_quote = quote3_finder[ch]
-                m = in_triple_quote.match(line, i + 3)
-                if m:
-                    # Remove the string & continue.
-                    end = m.span()[1]
-                    line = line[:i] + line[end:]
-                    in_doc = in_triple_quote = False
-                else:
-                    # Triple quote doesn't end on this line.
-                    break
-            else:
-                # At a single quote; remove the string & continue.
-                prev_line = line[:]
-                line = re.sub(quote1_finder[ch], ' ', line, 1)
-                # No more change detected, so be quiet or give up.
-                if prev_line == line:
-                    # Let's be quiet and hope only one line is affected.
-                    line = ''
deleted file mode 100644
--- a/trac-0.11/bitten/util/testrunner.py
+++ /dev/null
@@ -1,280 +0,0 @@
-# -*- coding: utf-8 -*-
-#
-# Copyright (C) 2005-2007 Christopher Lenz <cmlenz@gmx.de>
-# Copyright (C) 2008 Matt Good <matt@matt-good.net>
-# Copyright (C) 2008 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.
-
-from distutils import log
-from distutils.errors import DistutilsOptionError
-import os
-import re
-from StringIO import StringIO
-import sys
-import time
-from pkg_resources import Distribution, EntryPoint, PathMetadata, \
-                          normalize_path, require, working_set
-from setuptools.command.test import test
-from unittest import _TextTestResult, TextTestRunner
-
-from bitten import __version__ as VERSION
-from bitten.util import xmlio
-
-__docformat__ = 'restructuredtext en'
-
-
-class XMLTestResult(_TextTestResult):
-
-    def __init__(self, stream, descriptions, verbosity):
-        _TextTestResult.__init__(self, stream, descriptions, verbosity)
-        self.tests = []
-
-    def startTest(self, test):
-        _TextTestResult.startTest(self, test)
-        filename = sys.modules[test.__module__].__file__
-        if filename.endswith('.pyc') or filename.endswith('.pyo'):
-            filename = filename[:-1]
-        self.tests.append([test, filename, time.time(), None, None])
-
-    def stopTest(self, test):
-        self.tests[-1][2] = time.time() - self.tests[-1][2]
-        _TextTestResult.stopTest(self, test)
-
-
-class XMLTestRunner(TextTestRunner):
-
-    def __init__(self, stream=sys.stdout, xml_stream=None):
-        TextTestRunner.__init__(self, stream, descriptions=0, verbosity=2)
-        self.xml_stream = xml_stream
-
-    def _makeResult(self):
-        return XMLTestResult(self.stream, self.descriptions, self.verbosity)
-
-    def run(self, test):
-        result = TextTestRunner.run(self, test)
-        if not self.xml_stream:
-            return result
-
-        root = xmlio.Element('unittest-results')
-        for testcase, filename, timetaken, stdout, stderr in result.tests:
-            status = 'success'
-            tb = None
-
-            if testcase in [e[0] for e in result.errors]:
-                status = 'error'
-                tb = [e[1] for e in result.errors if e[0] is testcase][0]
-            elif testcase in [f[0] for f in result.failures]:
-                status = 'failure'
-                tb = [f[1] for f in result.failures if f[0] is testcase][0]
-
-            name = str(testcase)
-            fixture = None
-            description = testcase.shortDescription() or ''
-            if description.startswith('doctest of '):
-                name = 'doctest'
-                fixture = description[11:]
-                description = None
-            else:
-                match = re.match('(\w+)\s+\(([\w.]+)\)', name)
-                if match:
-                    name = match.group(1)
-                    fixture = match.group(2)
-
-            test_elem = xmlio.Element('test', file=filename, name=name,
-                                      fixture=fixture, status=status,
-                                      duration=timetaken)
-            if description:
-                test_elem.append(xmlio.Element('description')[description])
-            if stdout:
-                test_elem.append(xmlio.Element('stdout')[stdout])
-            if stderr:
-                test_elem.append(xmlio.Element('stdout')[stderr])
-            if tb:         
-                test_elem.append(xmlio.Element('traceback')[tb])
-            root.append(test_elem)
-
-        root.write(self.xml_stream, newlines=True)
-        return result
-
-
-class unittest(test):
-    description = test.description + ', and optionally record code coverage'
-
-    user_options = test.user_options + [
-        ('xml-output=', None,
-            "Path to the XML file where test results are written to"),
-        ('coverage-dir=', None,
-            "Directory where coverage files are to be stored"),
-        ('coverage-summary=', None,
-            "Path to the file where the coverage summary should be stored"),
-        ('coverage-method=', None,
-            "Whether to use trace.py or coverage.py to collect code coverage. "
-            "Valid options are 'trace' (the default) or 'coverage'.")
-    ]
-
-    def initialize_options(self):
-        test.initialize_options(self)
-        self.xml_output = None
-        self.xml_output_file = None
-        self.coverage_summary = None
-        self.coverage_dir = None
-        self.coverage_method = 'trace'
-
-    def finalize_options(self):
-        test.finalize_options(self)
-
-        if self.xml_output is not None:
-            output_dir = os.path.dirname(self.xml_output) or '.'
-            if not os.path.exists(output_dir):
-                os.makedirs(output_dir)
-            self.xml_output_file = open(self.xml_output, 'w')
-
-        if self.coverage_method not in ('trace', 'coverage', 'figleaf'):
-            raise DistutilsOptionError('Unknown coverage method %r' %
-                                       self.coverage_method)
-
-    def run_tests(self):
-        if self.coverage_summary:
-            if self.coverage_method == 'coverage':
-                self._run_with_coverage()
-            elif self.coverage_method == 'figleaf':
-                self._run_with_figleaf()
-            else:
-                self._run_with_trace()
-        else:
-            self._run_tests()
-
-    def _run_with_figleaf(self):
-        import figleaf
-        figleaf.start()
-        try:
-            self._run_tests()
-        finally:
-            figleaf.stop()
-            figleaf.write_coverage(self.coverage_summary)
-
-    def _run_with_coverage(self):
-        import coverage
-        coverage.use_cache(False)
-        coverage.start()
-        try:
-            self._run_tests()
-        finally:
-            coverage.stop()
-
-            modules = [m for _, m in sys.modules.items()
-                       if m is not None and hasattr(m, '__file__')
-                       and os.path.splitext(m.__file__)[-1] in ('.py', '.pyc')]
-
-            # Generate summary file
-            buf = StringIO()
-            coverage.report(modules, file=buf)
-            buf.seek(0)
-            fileobj = open(self.coverage_summary, 'w')
-            try:
-                filter_coverage(buf, fileobj)
-            finally:
-                fileobj.close()
-
-            if self.coverage_dir:
-                if not os.path.exists(self.coverage_dir):
-                    os.makedirs(self.coverage_dir)
-                coverage.annotate(modules, directory=self.coverage_dir,
-                                  ignore_errors=True)
-
-    def _run_with_trace(self):
-        from trace import Trace
-        trace = Trace(ignoredirs=[sys.prefix, sys.exec_prefix], trace=False,
-                      count=True)
-        try:
-            trace.runfunc(self._run_tests)
-        finally:
-            results = trace.results()
-            real_stdout = sys.stdout
-            sys.stdout = open(self.coverage_summary, 'w')
-            try:
-                results.write_results(show_missing=True, summary=True,
-                                      coverdir=self.coverage_dir)
-            finally:
-                sys.stdout.close()
-                sys.stdout = real_stdout
-
-    def _run_tests(self):
-        old_path = sys.path[:]
-        ei_cmd = self.get_finalized_command("egg_info")
-        path_item = normalize_path(ei_cmd.egg_base)
-        metadata = PathMetadata(
-            path_item, normalize_path(ei_cmd.egg_info)
-        )
-        dist = Distribution(path_item, metadata, project_name=ei_cmd.egg_name)
-        working_set.add(dist)
-        require(str(dist.as_requirement()))
-        loader_ep = EntryPoint.parse("x=" + self.test_loader)
-        loader_class = loader_ep.load(require=False)
-
-        try:
-            import unittest
-            unittest.main(
-                None, None, [unittest.__file__] + self.test_args,
-                testRunner=XMLTestRunner(stream=sys.stdout,
-                                         xml_stream=self.xml_output_file),
-                testLoader=loader_class()
-            )
-        except SystemExit, e:
-            return e.code
-
-
-def filter_coverage(infile, outfile):
-    for idx, line in enumerate(infile):
-        if idx < 2 or line.startswith('--'):
-            outfile.write(line)
-            continue
-        parts = line.split()
-        name = parts[0]
-        if name == 'TOTAL':
-            continue
-        if name not in sys.modules:
-            outfile.write(line)
-            continue
-        filename = os.path.normpath(sys.modules[name].__file__)
-        if filename.endswith('.pyc') or filename.endswith('.pyo'):
-            filename = filename[:-1]
-        outfile.write(line.rstrip() + ' ' + filename + '\n')
-
-
-def main():
-    from distutils.dist import Distribution
-    from optparse import OptionParser
-
-    parser = OptionParser(usage='usage: %prog [options] test_suite ...',
-                          version='%%prog %s' % VERSION)
-    parser.add_option('-o', '--xml-output', action='store', dest='xml_output',
-                      metavar='FILE', help='write XML test results to FILE')
-    parser.add_option('-d', '--coverage-dir', action='store',
-                      dest='coverage_dir', metavar='DIR',
-                      help='store coverage results in DIR')
-    parser.add_option('-s', '--coverage-summary', action='store',
-                      dest='coverage_summary', metavar='FILE',
-                      help='write coverage summary to FILE')
-    options, args = parser.parse_args()
-    if len(args) < 1:
-        parser.error('incorrect number of arguments')
-
-    cmd = unittest(Distribution())
-    cmd.initialize_options()
-    cmd.test_suite = args[0]
-    if hasattr(options, 'xml_output'):
-        cmd.xml_output = options.xml_output
-    if hasattr(options, 'coverage_summary'):
-        cmd.coverage_summary = options.coverage_summary
-    if hasattr(options, 'coverage_dir'):
-        cmd.coverage_dir = options.coverage_dir
-    cmd.finalize_options()
-    cmd.run()
-
-if __name__ == '__main__':
-    main(sys.argv)
deleted file mode 100644
--- a/trac-0.11/bitten/util/tests/__init__.py
+++ /dev/null
@@ -1,22 +0,0 @@
-# -*- 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 doctest
-import unittest
-
-from bitten.util import xmlio
-
-def suite():
-    suite = unittest.TestSuite()
-    suite.addTest(doctest.DocTestSuite(xmlio))
-    return suite
-
-if __name__ == '__main__':
-    unittest.main(defaultTest='suite')
deleted file mode 100644
--- a/trac-0.11/bitten/util/xmlio.py
+++ /dev/null
@@ -1,318 +0,0 @@
-# -*- 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.
-
-"""Utility code for easy input and output of XML.
-
-The current implementation uses `xml.dom.minidom` under the hood for parsing.
-"""
-
-import os
-try:
-    from cStringIO import StringIO
-except ImportError:
-    from StringIO import StringIO
-from UserDict import DictMixin
-
-import cgi
-import string
-
-__trans = string.maketrans ("", "")
-__todel = ""
-for c in range (0, 256):
-    c1 = chr (c)
-    if not c1 in string.printable:
-        __todel += c1
-del c, c1
-
-__all__ = ['Fragment', 'Element', 'ParsedElement', 'parse']
-__docformat__ = 'restructuredtext en'
-
-def _escape_text(text):
-    """Escape special characters in the provided text so that it can be safely
-    included in XML text nodes.
-    """
-    return cgi.escape (str(text)).translate (__trans, __todel)
-
-def _escape_attr(attr):
-    """Escape special characters in the provided text so that it can be safely
-    included in XML attribute values.
-    """
-    return _escape_text(attr).replace('"', '&#34;')
-
-
-class Fragment(object):
-    """A collection of XML elements."""
-    __slots__ = ['children']
-
-    def __init__(self):
-        """Create an XML fragment."""
-        self.children = []
-
-    def __getitem__(self, nodes):
-        """Add nodes to the fragment."""
-        if not isinstance(nodes, (list, tuple)):
-            nodes = [nodes]
-        for node in nodes:
-            self.append(node)
-        return self
-
-    def __str__(self):
-        """Return a string representation of the XML fragment."""
-        buf = StringIO()
-        self.write(buf)
-        return buf.getvalue()
-
-    def append(self, node):
-        """Append an element or fragment as child."""
-        if isinstance(node, Element):
-            self.children.append(node)
-        elif isinstance(node, Fragment):
-            self.children += node.children
-        elif node is not None and node != '':
-            self.children.append(str(node))
-
-    def write(self, out, newlines=False):
-        """Serializes the element and writes the XML to the given output
-        stream.
-        """
-        for child in self.children:
-            if isinstance(child, (Element, ParsedElement)):
-                child.write(out, newlines=newlines)
-            else:
-                if child.startswith('<'):
-                    out.write('<![CDATA[' + child + ']]>')
-                else:
-                    out.write(_escape_text(child))
-
-
-class Element(Fragment):
-    """Simple XML output generator based on the builder pattern.
-
-    Construct XML elements by passing the tag name to the constructor:
-
-    >>> print Element('foo')
-    <foo/>
-
-    Attributes can be specified using keyword arguments. The values of the
-    arguments will be converted to strings and any special XML characters
-    escaped:
-
-    >>> print Element('foo', bar=42)
-    <foo bar="42"/>
-    >>> print Element('foo', bar='1 < 2')
-    <foo bar="1 &lt; 2"/>
-    >>> print Element('foo', bar='"baz"')
-    <foo bar="&#34;baz&#34;"/>
-
-    The order in which attributes are rendered is undefined.
-
-    Elements can be using item access notation:
-
-    >>> print Element('foo')[Element('bar'), Element('baz')]
-    <foo><bar/><baz/></foo>
-
-    Text nodes can be nested in an element by using strings instead of elements
-    in item access. Any special characters in the strings are escaped
-    automatically:
-
-    >>> print Element('foo')['Hello world']
-    <foo>Hello world</foo>
-    >>> print Element('foo')[42]
-    <foo>42</foo>
-    >>> print Element('foo')['1 < 2']
-    <foo>1 &lt; 2</foo>
-
-    This technique also allows mixed content:
-
-    >>> print Element('foo')['Hello ', Element('b')['world']]
-    <foo>Hello <b>world</b></foo>
-
-    Finally, text starting with an opening angle bracket is treated specially:
-    under the assumption that the text actually contains XML itself, the whole
-    thing is wrapped in a CDATA block instead of escaping all special characters
-    individually:
-
-    >>> print Element('foo')['<bar a="3" b="4"><baz/></bar>']
-    <foo><![CDATA[<bar a="3" b="4"><baz/></bar>]]></foo>
-    """
-    __slots__ = ['name', 'attr']
-
-    def __init__(self, name_, **attr):
-        """Create an XML element using the specified tag name.
-        
-        The tag name must be supplied as the first positional argument. All
-        keyword arguments following it are handled as attributes of the element.
-        """
-        Fragment.__init__(self)
-        self.name = name_
-        self.attr = dict([(name, value) for name, value in attr.items()
-                          if value is not None])
-
-    def write(self, out, newlines=False):
-        """Serializes the element and writes the XML to the given output
-        stream.
-        """
-        out.write('<')
-        out.write(self.name)
-        for name, value in self.attr.items():
-            out.write(' %s="%s"' % (name, _escape_attr(value)))
-        if self.children:
-            out.write('>')
-            Fragment.write(self, out, newlines)
-            out.write('</' + self.name + '>')
-        else:
-            out.write('/>')
-        if newlines:
-            out.write(os.linesep)
-
-
-class ParseError(Exception):
-    """Exception thrown when there's an error parsing an XML document."""
-
-
-def parse(text_or_file):
-    """Parse an XML document provided as string or file-like object.
-    
-    Returns an instance of `ParsedElement` that can be used to traverse the
-    parsed document.
-    """
-    from xml.dom import minidom
-    from xml.parsers import expat
-    try:
-        if isinstance(text_or_file, (str, unicode)):
-            dom = minidom.parseString(text_or_file)
-        else:
-            dom = minidom.parse(text_or_file)
-        return ParsedElement(dom.documentElement)
-    except expat.error, e:
-        raise ParseError(e)
-
-
-class ParsedElement(object):
-    """Representation of an XML element that was parsed from a string or
-    file.
-    
-    This class should not be used directly. Rather, XML text parsed using
-    `xmlio.parse()` will return an instance of this class.
-    
-    >>> xml = parse('<root/>')
-    >>> print xml.name
-    root
-    
-    Parsed elements can be serialized to a string using the `write()` method:
-    
-    >>> import sys
-    >>> parse('<root></root>').write(sys.stdout)
-    <root/>
-    
-    For convenience, this is also done when coercing the object to a string
-    using the builtin ``str()`` function, which is used when printing an
-    object:
-    
-    >>> print parse('<root></root>')
-    <root/>
-    
-    (Note that serializing the element will produce a normalized representation
-    that may not excatly match the input string.)
-    
-    Attributes are accessed via the `attr` member:
-    
-    >>> print parse('<root foo="bar"/>').attr['foo']
-    bar
-    
-    Attributes can also be updated, added or removed:
-    
-    >>> xml = parse('<root foo="bar"/>')
-    >>> xml.attr['foo'] = 'baz'
-    >>> print xml
-    <root foo="baz"/>
-
-    >>> del xml.attr['foo']
-    >>> print xml
-    <root/>
-
-    >>> xml.attr['foo'] = 'bar'
-    >>> print xml
-    <root foo="bar"/>
-
-    CDATA sections are included in the text content of the element returned by
-    `gettext()`:
-    
-    >>> xml = parse('<root>foo<![CDATA[ <bar> ]]>baz</root>')
-    >>> xml.gettext()
-    'foo <bar> baz'
-    """
-    __slots__ = ['_node', 'attr']
-
-    class _Attrs(DictMixin):
-        """Simple wrapper around the element attributes to provide a dictionary
-        interface."""
-        def __init__(self, node):
-            self._node = node
-        def __getitem__(self, name):
-            attr = self._node.getAttributeNode(name)
-            if not attr:
-                raise KeyError(name)
-            return attr.value.encode('utf-8')
-        def __setitem__(self, name, value):
-            self._node.setAttribute(name, value)
-        def __delitem__(self, name):
-            self._node.removeAttribute(name)
-        def keys(self):
-            return [key.encode('utf-8') for key in self._node.attributes.keys()]
-
-    def __init__(self, node):
-        self._node = node
-        self.attr = ParsedElement._Attrs(node)
-
-    name = property(fget=lambda self: self._node.localName,
-                    doc='Local name of the element')
-    namespace = property(fget=lambda self: self._node.namespaceURI,
-                         doc='Namespace URI of the element')
-
-    def children(self, name=None):
-        """Iterate over the child elements of this element.
-
-        If the parameter `name` is provided, only include elements with a
-        matching local name. Otherwise, include all elements.
-        """
-        for child in [c for c in self._node.childNodes if c.nodeType == 1]:
-            if name in (None, child.tagName):
-                yield ParsedElement(child)
-
-    def __iter__(self):
-        return self.children()
-
-    def gettext(self):
-        """Return the text content of this element.
-        
-        This concatenates the values of all text and CDATA nodes that are
-        immediate children of this element.
-        """
-        return ''.join([c.nodeValue.encode('utf-8')
-                        for c in self._node.childNodes
-                        if c.nodeType in (3, 4)])
-
-    def write(self, out, newlines=False):
-        """Serializes the element and writes the XML to the given output
-        stream.
-        """
-        self._node.writexml(out, newl=newlines and '\n' or '')
-
-    def __str__(self):
-        """Return a string representation of the XML element."""
-        buf = StringIO()
-        self.write(buf)
-        return buf.getvalue()
-
-
-if __name__ == '__main__':
-    import doctest
-    doctest.testmod()
deleted file mode 100644
--- a/trac-0.11/bitten/web_ui.py
+++ /dev/null
@@ -1,626 +0,0 @@
-# -*- 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.
-
-"""Implementation of the Bitten web interface."""
-
-from datetime import datetime
-import posixpath
-import re
-from StringIO import StringIO
-
-import pkg_resources
-from genshi.builder import tag
-from trac.core import *
-from trac.timeline import ITimelineEventProvider
-from trac.util import escape, pretty_timedelta, format_datetime, shorten_line, \
-                      Markup
-from trac.util.html import html
-from trac.web import IRequestHandler
-from trac.web.chrome import INavigationContributor, ITemplateProvider, \
-                            add_link, add_stylesheet, add_ctxtnav, \
-                            prevnext_nav, add_script
-from trac.wiki import wiki_to_html, wiki_to_oneliner
-from bitten.api import ILogFormatter, IReportChartGenerator, IReportSummarizer
-from bitten.model import BuildConfig, TargetPlatform, Build, BuildStep, \
-                         BuildLog, Report
-from bitten.queue import collect_changes
-
-_status_label = {Build.PENDING: 'pending',
-                 Build.IN_PROGRESS: 'in progress',
-                 Build.SUCCESS: 'completed',
-                 Build.FAILURE: 'failed'}
-
-def _get_build_data(env, req, build):
-    data = {'id': build.id, 'name': build.slave, 'rev': build.rev,
-            'status': _status_label[build.status],
-            'cls': _status_label[build.status].replace(' ', '-'),
-            'href': req.href.build(build.config, build.id),
-            'chgset_href': req.href.changeset(build.rev)}
-    if build.started:
-        data['started'] = format_datetime(build.started)
-        data['started_delta'] = pretty_timedelta(build.started)
-        data['duration'] = pretty_timedelta(build.started)
-    if build.stopped:
-        data['stopped'] = format_datetime(build.stopped)
-        data['stopped_delta'] = pretty_timedelta(build.stopped)
-        data['duration'] = pretty_timedelta(build.stopped, build.started)
-    data['slave'] = {
-        'name': build.slave,
-        'ipnr': build.slave_info.get(Build.IP_ADDRESS),
-        'os_name': build.slave_info.get(Build.OS_NAME),
-        'os_family': build.slave_info.get(Build.OS_FAMILY),
-        'os_version': build.slave_info.get(Build.OS_VERSION),
-        'machine': build.slave_info.get(Build.MACHINE),
-        'processor': build.slave_info.get(Build.PROCESSOR)
-    }
-    return data
-
-
-class BittenChrome(Component):
-    """Provides the Bitten templates and static resources."""
-
-    implements(INavigationContributor, ITemplateProvider)
-
-    # INavigationContributor methods
-
-    def get_active_navigation_item(self, req):
-        """Called by Trac to determine which navigation item should be marked
-        as active.
-        
-        :param req: the request object
-        """
-        return 'build'
-
-    def get_navigation_items(self, req):
-        """Return the navigation item for access the build status overview from
-        the Trac navigation bar."""
-        if 'BUILD_VIEW' in req.perm:
-            yield ('mainnav', 'build',
-                   tag.a('Builds Status', href=req.href.build(), accesskey=5))
-
-    # ITemplatesProvider methods
-
-    def get_htdocs_dirs(self):
-        """Return the directories containing static resources."""
-        return [('bitten', pkg_resources.resource_filename(__name__, 'htdocs'))]
-
-    def get_templates_dirs(self):
-        """Return the directories containing templates."""
-        return [pkg_resources.resource_filename(__name__, 'templates')]
-
-
-class BuildConfigController(Component):
-    """Implements the web interface for build configurations."""
-
-    implements(IRequestHandler)
-
-    # IRequestHandler methods
-
-    def match_request(self, req):
-        match = re.match(r'/build(?:/([\w.-]+))?/?$', req.path_info)
-        if match:
-            if match.group(1):
-                req.args['config'] = match.group(1)
-            return True
-
-    def process_request(self, req):
-        req.perm.require('BUILD_VIEW')
-
-        action = req.args.get('action')
-        view = req.args.get('view')
-        config = req.args.get('config')
-
-        if config:
-            data = self._render_config(req, config)
-        elif view == 'inprogress':
-            data = self._render_inprogress(req)
-        else:
-            data = self._render_overview(req)
-
-        add_stylesheet(req, 'bitten/bitten.css')
-        return 'bitten_config.html', data, None
-
-    # Internal methods
-
-    def _render_overview(self, req):
-        data = {'title': 'Build Status'}
-        show_all = False
-        if req.args.get('show') == 'all':
-            show_all = True
-        data['show_all'] = show_all
-
-        configs = []
-        for config in BuildConfig.select(self.env, include_inactive=show_all):
-            description = config.description
-            if description:
-                description = wiki_to_html(description, self.env, req)
-            config_data = {
-                'name': config.name, 'label': config.label or config.name,
-                'active': config.active, 'path': config.path,
-                'description': description,
-                'href': req.href.build(config.name),
-                'builds': []
-            }
-            configs.append(config_data)
-            if not config.active:
-                continue
-
-            repos = self.env.get_repository(req.authname)
-            if hasattr(repos, 'sync'):
-                repos.sync()
-
-            prev_rev = None
-            for platform, rev, build in collect_changes(repos, config):
-                if rev != prev_rev:
-                    if prev_rev is None:
-                        chgset = repos.get_changeset(rev)
-                        config_data['youngest_rev'] = {
-                            'id': rev, 'href': req.href.changeset(rev),
-                            'author': chgset.author or 'anonymous',
-                            'date': format_datetime(chgset.date),
-                            'message': wiki_to_oneliner(
-                                shorten_line(chgset.message), self.env, req=req)
-                        }
-                    else:
-                        break
-                    prev_rev = rev
-                if build:
-                    build_data = _get_build_data(self.env, req, build)
-                    build_data['platform'] = platform.name
-                    config_data['builds'].append(build_data)
-                else:
-                    config_data['builds'].append({
-                        'platform': platform.name, 'status': 'pending'
-                    })
-
-        data['configs'] = configs
-        data['page_mode'] = 'overview'
-        add_link(req, 'views', req.href.build(view='inprogress'),
-                 'In Progress Builds')
-        add_ctxtnav(req, 'In Progress Builds',
-                    req.href.build(view='inprogress'))
-        return data
-
-    def _render_inprogress(self, req):
-        data = {'title': 'In Progress Builds',
-                'page_mode': 'view-inprogress'}
-
-        db = self.env.get_db_cnx()
-
-        configs = []
-        for config in BuildConfig.select(self.env, include_inactive=False):
-            self.log.debug(config.name)
-            if not config.active:
-                continue
-
-            in_progress_builds = Build.select(self.env, config=config.name,
-                                              status=Build.IN_PROGRESS, db=db)
-
-            current_builds = 0
-            builds = []
-            # sort correctly by revision.
-            for build in sorted(in_progress_builds,
-                                cmp=lambda x, y: int(y.rev) - int(x.rev)):
-                rev = build.rev
-                build_data = _get_build_data(self.env, req, build)
-                build_data['rev'] = rev
-                build_data['rev_href'] = req.href.changeset(rev)
-                platform = TargetPlatform.fetch(self.env, build.platform)
-                build_data['platform'] = platform.name
-                build_data['steps'] = []
-
-                for step in BuildStep.select(self.env, build=build.id, db=db):
-                    build_data['steps'].append({
-                        'name': step.name,
-                        'description': step.description,
-                        'duration': datetime.fromtimestamp(step.stopped) - \
-                                    datetime.fromtimestamp(step.started),
-                        'failed': not step.successful,
-                        'errors': step.errors,
-                        'href': build_data['href'] + '#step_' + step.name
-                    })
-
-                builds.append(build_data)
-                current_builds += 1
-
-            if current_builds == 0: 
-                continue
-
-            description = config.description
-            if description:
-                description = wiki_to_html(description, self.env, req)
-            configs.append({
-                'name': config.name, 'label': config.label or config.name,
-                'active': config.active, 'path': config.path,
-                'description': description,
-                'href': req.href.build(config.name),
-                'builds': builds
-            })
-
-        data['configs'] = configs
-        return data
-
-    def _render_config(self, req, config_name):
-        db = self.env.get_db_cnx()
-
-        config = BuildConfig.fetch(self.env, config_name, db=db)
-        data = {'title': 'Build Configuration "%s"' \
-                          % config.label or config.name,
-                'page_mode': 'view_config'}
-        add_link(req, 'up', req.href.build(), 'Build Status')
-        description = config.description
-        if description:
-            description = wiki_to_html(description, self.env, req)
-        data['config'] = {
-            'name': config.name, 'label': config.label, 'path': config.path,
-            'min_rev': config.min_rev,
-            'min_rev_href': req.href.changeset(config.min_rev),
-            'max_rev': config.max_rev,
-            'max_rev_href': req.href.changeset(config.max_rev),
-            'active': config.active, 'description': description,
-            'browser_href': req.href.browser(config.path)
-        }
-
-        platforms = list(TargetPlatform.select(self.env, config=config_name,
-                                               db=db))
-        data['config']['platforms'] = [
-            {'name': platform.name, 'id': platform.id}
-            for platform in platforms
-        ]
-
-        has_reports = False
-        for report in Report.select(self.env, config=config.name, db=db):
-            has_reports = True
-            break
-
-        if has_reports:
-            chart_generators = []
-            for generator in ReportChartController(self.env).generators: 
-                for category in generator.get_supported_categories(): 
-                    chart_generators.append({
-                        'href': req.href.build(config.name, 'chart/' + category) 
-                    })
-            data['config']['charts'] = chart_generators 
-            charts_license = self.config.get('bitten', 'charts_license')
-            if charts_license:
-                data['config']['charts_license'] = charts_license
-
-        page = max(1, int(req.args.get('page', 1)))
-        more = False
-        data['page_number'] = page
-
-        repos = self.env.get_repository(req.authname)
-        if hasattr(repos, 'sync'):
-            repos.sync()
-
-        builds_per_page = 12 * len(platforms)
-        idx = 0
-        builds = {}
-        for platform, rev, build in collect_changes(repos, config):
-            if idx >= page * builds_per_page:
-                more = True
-                break
-            elif idx >= (page - 1) * builds_per_page:
-                builds.setdefault(rev, {})
-                builds[rev].setdefault('href', req.href.changeset(rev))
-                if build and build.status != Build.PENDING:
-                    build_data = _get_build_data(self.env, req, build)
-                    build_data['steps'] = []
-                    for step in BuildStep.select(self.env, build=build.id,
-                                                 db=db):
-                        build_data['steps'].append({
-                            'name': step.name,
-                            'description': step.description,
-                            'duration': datetime.fromtimestamp(step.stopped) - \
-                                        datetime.fromtimestamp(step.started),
-                            'failed': not step.successful,
-                            'errors': step.errors,
-                            'href': build_data['href'] + '#step_' + step.name
-                        })
-                    builds[rev][platform.id] = build_data
-            idx += 1
-        data['config']['builds'] = builds
-
-        if page > 1:
-            if page == 2:
-                prev_href = req.href.build(config.name)
-            else:
-                prev_href = req.href.build(config.name, page=page - 1)
-            add_link(req, 'prev', prev_href, 'Previous Page')
-        if more:
-            next_href = req.href.build(config.name, page=page + 1)
-            add_link(req, 'next', next_href, 'Next Page')
-        prevnext_nav(req, 'Page')
-        return data
-
-
-class BuildController(Component):
-    """Renders the build page."""
-    implements(INavigationContributor, IRequestHandler, ITimelineEventProvider)
-
-    log_formatters = ExtensionPoint(ILogFormatter)
-    report_summarizers = ExtensionPoint(IReportSummarizer)
-
-    # INavigationContributor methods
-
-    def get_active_navigation_item(self, req):
-        return 'build'
-
-    def get_navigation_items(self, req):
-        return []
-
-    # IRequestHandler methods
-
-    def match_request(self, req):
-        match = re.match(r'/build/([\w.-]+)/(\d+)', req.path_info)
-        if match:
-            if match.group(1):
-                req.args['config'] = match.group(1)
-                if match.group(2):
-                    req.args['id'] = match.group(2)
-            return True
-
-    def process_request(self, req):
-        req.perm.require('BUILD_VIEW')
-
-        db = self.env.get_db_cnx()
-        build_id = int(req.args.get('id'))
-        build = Build.fetch(self.env, build_id, db=db)
-        assert build, 'Build %s does not exist' % build_id
-
-        if req.method == 'POST':
-            if req.args.get('action') == 'invalidate':
-                self._do_invalidate(req, build, db)
-            req.redirect(req.href.build(build.config, build.id))
-
-        add_link(req, 'up', req.href.build(build.config),
-                 'Build Configuration')
-        status2title = {Build.SUCCESS: 'Success', Build.FAILURE: 'Failure',
-                        Build.IN_PROGRESS: 'In Progress'}
-        data = {'title': 'Build %s - %s' % (build_id,
-                                            status2title[build.status]),
-                'page_mode': 'view_build',
-                'build': {}}
-        config = BuildConfig.fetch(self.env, build.config, db=db)
-        data['build']['config'] = {
-            'name': config.label,
-            'href': req.href.build(config.name)
-        }
-
-        formatters = []
-        for formatter in self.log_formatters:
-            formatters.append(formatter.get_formatter(req, build))
-
-        summarizers = {} # keyed by report type
-        for summarizer in self.report_summarizers:
-            categories = summarizer.get_supported_categories()
-            summarizers.update(dict([(cat, summarizer) for cat in categories]))
-
-        data['build'].update(_get_build_data(self.env, req, build))
-        steps = []
-        for step in BuildStep.select(self.env, build=build.id, db=db):
-            steps.append({
-                'name': step.name, 'description': step.description,
-                'duration': pretty_timedelta(step.started, step.stopped),
-                'failed': step.status == BuildStep.FAILURE,
-                'errors': step.errors,
-                'log': self._render_log(req, build, formatters, step),
-                'reports': self._render_reports(req, config, build, summarizers,
-                                                step)
-            })
-        data['build']['steps'] = steps
-        data['build']['can_delete'] = ('BUILD_DELETE' in req.perm)
-
-        repos = self.env.get_repository(req.authname)
-        chgset = repos.get_changeset(build.rev)
-        data['build']['chgset_author'] = chgset.author
-
-        add_script(req, 'bitten/tabset.js')
-        add_stylesheet(req, 'bitten/bitten.css')
-        return 'bitten_build.html', data, None
-
-    # ITimelineEventProvider methods
-
-    def get_timeline_filters(self, req):
-        if 'BUILD_VIEW' in req.perm:
-            yield ('build', 'Builds')
-
-    def get_timeline_events(self, req, start, stop, filters):
-        if 'build' not in filters:
-            return
-
-        if isinstance(start, datetime): # Trac>=0.11
-            from trac.util.datefmt import to_timestamp
-            start = to_timestamp(start)
-            stop = to_timestamp(stop)
-
-        add_stylesheet(req, 'bitten/bitten.css')
-
-        db = self.env.get_db_cnx()
-        cursor = db.cursor()
-        cursor.execute("SELECT b.id,b.config,c.label,b.rev,p.name,"
-                       "b.stopped,b.status FROM bitten_build AS b"
-                       "  INNER JOIN bitten_config AS c ON (c.name=b.config) "
-                       "  INNER JOIN bitten_platform AS p ON (p.id=b.platform) "
-                       "WHERE b.stopped>=%s AND b.stopped<=%s "
-                       "AND b.status IN (%s, %s) ORDER BY b.stopped",
-                       (start, stop, Build.SUCCESS, Build.FAILURE))
-
-        event_kinds = {Build.SUCCESS: 'successbuild',
-                       Build.FAILURE: 'failedbuild'}
-        for id, config, label, rev, platform, stopped, status in cursor:
-
-            errors = []
-            if status == Build.FAILURE:
-                for step in BuildStep.select(self.env, build=id,
-                                             status=BuildStep.FAILURE,
-                                             db=db):
-                    errors += [(step.name, error) for error
-                               in step.errors]
-
-            title = tag('Build of ', tag.em('%s [%s]' % (label, rev)),
-                        ' on %s %s' % (platform, _status_label[status]))
-            message = ''
-            if req.args.get('format') == 'rss':
-                href = req.abs_href.build(config, id)
-                if errors:
-                    buf = StringIO()
-                    prev_step = None
-                    for step, error in errors:
-                        if step != prev_step:
-                            if prev_step is not None:
-                                buf.write('</ul>')
-                            buf.write('<p>Step %s failed:</p><ul>' \
-                                      % escape(step))
-                            prev_step = step
-                        buf.write('<li>%s</li>' % escape(error))
-                    buf.write('</ul>')
-                    message = Markup(buf.getvalue())
-            else:
-                href = req.href.build(config, id)
-                if errors:
-                    steps = []
-                    for step, error in errors:
-                        if step not in steps:
-                            steps.append(step)
-                    steps = [Markup('<em>%s</em>') % step for step in steps]
-                    if len(steps) < 2:
-                        message = steps[0]
-                    elif len(steps) == 2:
-                        message = Markup(' and ').join(steps)
-                    elif len(steps) > 2:
-                        message = Markup(', ').join(steps[:-1]) + ', and ' + \
-                                  steps[-1]
-                    message = Markup('Step%s %s failed') % (
-                        len(steps) != 1 and 's' or '', message
-                    )
-            yield event_kinds[status], href, title, stopped, None, message
-
-    # Internal methods
-
-    def _do_invalidate(self, req, build, db):
-        self.log.info('Invalidating build %d', build.id)
-
-        for step in BuildStep.select(self.env, build=build.id, db=db):
-            step.delete(db=db)
-
-        build.slave = None
-        build.started = build.stopped = 0
-        build.status = Build.PENDING
-        build.slave_info = {}
-        build.update()
-
-        db.commit()
-
-        req.redirect(req.href.build(build.config))
-
-    def _render_log(self, req, build, formatters, step):
-        items = []
-        for log in BuildLog.select(self.env, build=build.id, step=step.name):
-            for level, message in log.messages:
-                for format in formatters:
-                    message = format(step, log.generator, level, message)
-                items.append({'level': level, 'message': message})
-        return items
-
-    def _render_reports(self, req, config, build, summarizers, step):
-        reports = []
-        for report in Report.select(self.env, build=build.id, step=step.name):
-            summarizer = summarizers.get(report.category)
-            if summarizer:
-                tmpl, data = summarizer.render_summary(req, config, build,
-                                                        step, report.category)
-            else:
-                tmpl = data = None
-            reports.append({'category': report.category,
-                            'template': tmpl, 'data': data})
-        return reports
-
-
-class ReportChartController(Component):
-    implements(IRequestHandler)
-
-    generators = ExtensionPoint(IReportChartGenerator)
-
-    # IRequestHandler methods
-
-    def match_request(self, req):
-        match = re.match(r'/build/([\w.-]+)/chart/(\w+)', req.path_info)
-        if match:
-            req.args['config'] = match.group(1)
-            req.args['category'] = match.group(2)
-            return True
-
-    def process_request(self, req):
-        category = req.args.get('category')
-        config = BuildConfig.fetch(self.env, name=req.args.get('config'))
-
-        for generator in self.generators:
-            if category in generator.get_supported_categories():
-                tmpl, data = generator.generate_chart_data(req, config,
-                                                           category)
-                break
-        else:
-            raise TracError('Unknown report category "%s"' % category)
-
-        return tmpl, data, 'text/xml'
-
-
-class SourceFileLinkFormatter(Component):
-    """Detects references to files in the build log and renders them as links
-    to the repository browser.
-    """
-
-    implements(ILogFormatter)
-
-    _fileref_re = re.compile('(?P<path>[\w.-]+(?:/[\w.-]+)+)(?P<line>(:\d+))?')
-
-    def get_formatter(self, req, build):
-        """Return the log message formatter function."""
-        config = BuildConfig.fetch(self.env, name=build.config)
-        repos = self.env.get_repository(req.authname)
-        href = req.href.browser
-        cache = {}
-
-        def _replace(m):
-            filepath = posixpath.normpath(m.group('path').replace('\\', '/'))
-            if not cache.get(filepath) is True:
-                parts = filepath.split('/')
-                path = ''
-                for part in parts:
-                    path = posixpath.join(path, part)
-                    if path not in cache:
-                        try:
-                            repos.get_node(posixpath.join(config.path, path),
-                                           build.rev)
-                            cache[path] = True
-                        except TracError:
-                            cache[path] = False
-                    if cache[path] is False:
-                        return m.group(0)
-            link = href(config.path, filepath)
-            if m.group('line'):
-                link += '#L' + m.group('line')[1:]
-            return Markup(tag.a(m.group(0), href=link))
-
-        def _formatter(step, type, level, message):
-            buf = []
-            offset = 0
-            for mo in self._fileref_re.finditer(message):
-                start, end = mo.span()
-                if start > offset:
-                    buf.append(message[offset:start])
-                buf.append(_replace(mo))
-                offset = end
-            if offset < len(message):
-                buf.append(message[offset:])
-            return Markup("").join(buf)
-
-        return _formatter
deleted file mode 100644
--- a/trac-0.11/doc/commands.txt
+++ /dev/null
@@ -1,890 +0,0 @@
-.. -*- mode: rst; encoding: utf-8 -*-
-
-=====================
-Build Recipe Commands
-=====================
-
-`Build recipes`_ are represented by XML documents. This page describes what
-commands are generally available in recipes. Please note, though, that
-third-party packages can add additional commands, which would then be
-documented by that third party.
-
-.. _`build recipes`: recipes.html
-
-.. contents:: Contents
-   :depth: 2
-.. sectnum::
-
-
-Generic Commands
-================
-
-These are commands that are used without a namespace prefix.
-
-
-------------
-``<report>``
-------------
-
-Parse an XML file and send it to the master as a report with a given category.
-Use this command in conjunction with the ``<sh:pipe>`` or ``<x:transform>``
-commands to send custom reports to the build master.
-
-Parameters
-----------
-
-+--------------+-------------------------------------------------------------+
-| Name         | Description                                                 |
-+==============+=============================================================+
-| ``category`` | Category of the report (for example "test" or "coverage").  |
-+--------------+-------------------------------------------------------------+
-| ``file``     | Path to the XML file containing the report data, relative   |
-|              | to the project directory.                                   |
-+--------------+-------------------------------------------------------------+
-
-Both parameters must be specified.
-
-
-Shell Tools
-===========
-
-A bundle of generic tools that are not specific to any programming language or
-tool-chain.
-
-:Namespace: ``http://bitten.cmlenz.net/tools/sh``
-:Common prefix: ``sh``
-
-
--------------
-``<sh:exec>``
--------------
-
-Executes a program or script.
-
-Parameters
-----------
-
-+----------------+-----------------------------------------------------------+
-| Name           | Description                                               |
-+================+===========================================================+
-| ``executable`` | The name of the executable program.                       |
-+----------------+-----------------------------------------------------------+
-| ``file``       | Path to the script to execute, relative to the project    |
-|                | directory                                                 |
-+----------------+-----------------------------------------------------------+
-| ``output``     | Path to the output file                                   |
-+----------------+-----------------------------------------------------------+
-| ``args``       | Any arguments to pass to the executable or script         |
-+----------------+-----------------------------------------------------------+
-
-Either ``executable`` or ``file`` must be specified.
-
-Examples
---------
-
-TODO
-
-
--------------
-``<sh:pipe>``
--------------
-
-Pipes the content of a file through a program or script.
-
-Parameters
-----------
-
-+----------------+-----------------------------------------------------------+
-| Name           | Description                                               |
-+================+===========================================================+
-| ``executable`` | The name of the executable program.                       |
-+----------------+-----------------------------------------------------------+
-| ``file``       | Path to the script to execute, relative to the project    |
-|                | directory                                                 |
-+----------------+-----------------------------------------------------------+
-| ``input``      | Path to the input file                                    |
-+----------------+-----------------------------------------------------------+
-| ``output``     | Path to the output file                                   |
-+----------------+-----------------------------------------------------------+
-| ``args``       | Any arguments to pass to the executable or script         |
-+----------------+-----------------------------------------------------------+
-
-Either ``executable`` or ``file`` must be specified.
-
-Examples
---------
-
-TODO
-
-
-C/Unix Tools
-============
-
-These commands provide support for tools commonly used for development of C/C++
-applications on Unix platforms, such as ``make``.
-
-:Namespace: ``http://bitten.cmlenz.net/tools/c``
-:Common prefix: ``c``
-
-
-------------------
-``<c:autoreconf>``
-------------------
-
-Executes ths autotool autoreconf.
-
-Parameters
-----------
-
-    :param force: consider all files obsolete
-    :param install: copy missing auxiliary files
-    :param symlink: install symbolic links instead of copies
-    :param warnings: report the warnings falling in CATEGORY
-    :prepend_include: prepend directories to search path
-    :include: append directories to search path
-
-
-+--------------+-------------------------------------------------------------+
-| Name         | Description                                                 |
-+==============+=============================================================+
-| ``force``    | Consider all files obsolete                                 |
-+--------------+-------------------------------------------------------------+
-| ``install``  | Copy missing auxiliary files                                |
-+--------------+-------------------------------------------------------------+
-| ``symlink``  | Install symbolic links instead of copies                    |
-+--------------+-------------------------------------------------------------+
-| ``warnings`` | Report the warnings related to category                     |
-|              | (which can actually be a comma separated list)              |
-+--------------+-------------------------------------------------------------+
-| ``prepend_include``  | Prepend directories to search path                  |
-+--------------+-------------------------------------------------------------+
-| ``include``   | Append directories to search path                          |
-+--------------+-------------------------------------------------------------+
-
-Examples
---------
-
-.. code-block:: xml
-
-  <c:autoreconf force="1" install="1" warnings="cross,syntax,error"/>
-
-Runs the ``autoreconf`` tool in the base directory with the option: force, install
-and 3 warning categories active: cross,syntax,error. This is equivalent to::
-
-  autoreconf --force --install --warnings=cross,syntax,error
-
-
------------------
-``<c:configure>``
------------------
-
-Executes a configure script as generated by Autoconf.
-
-Parameters
-----------
-
-+--------------+-------------------------------------------------------------+
-| Name         | Description                                                 |
-+==============+=============================================================+
-| ``file``     | Name of the configure script (defaults to "configure")      |
-+--------------+-------------------------------------------------------------+
-| ``enable``   | List of features to enable, separated by spaces.            |
-+--------------+-------------------------------------------------------------+
-| ``disable``  | List of features to disable, separated by spaces.           |
-+--------------+-------------------------------------------------------------+
-| ``with``     | List of packages to include, separated by spaces.           |
-+--------------+-------------------------------------------------------------+
-| ``without``  | List of packages to exclude, separated by spaces.           |
-+--------------+-------------------------------------------------------------+
-| ``cflags``   | Value of the `CFLAGS` variable to pass to the script.       |
-+--------------+-------------------------------------------------------------+
-| ``cxxflags`` | Value of the `CXXFLAGS` variable to pass to the script.     |
-+--------------+-------------------------------------------------------------+
-
-Examples
---------
-
-.. code-block:: xml
-
-  <c:configure enable="threadsafe" cflags="-O"/>
-
-Runs the ``configure`` script in the base directory, enable the ``threadsafe``
-feature, and passing ``-O`` as ``CFLAGS``. This is equivalent to::
-
-  ./configure --enable-threadsafe CFLAGS="-O"
-
-
-------------
-``<c:gcov>``
-------------
-
-Run gcov_ to extract coverage data where available.
-
-.. _gcov: http://gcc.gnu.org/onlinedocs/gcc/Gcov-Intro.html
-
-Parameters
-----------
-
-+--------------+------------------------------------------------------------+
-| Name         | Description                                                |
-+==============+============================================================+
-| ``include``  | List of glob patterns (separated by space) that specify    |
-|              | which source files should be included in the coverage      |
-|              | report                                                     |
-+--------------+------------------------------------------------------------+
-| ``exclude``  | List of glob patterns (separated by space) that specify    |
-|              | which source files should be excluded from the coverage    |
-|              | report                                                     |
-+--------------+------------------------------------------------------------+
-| ``prefix``   | Optional prefix name that is added to object files by the  |
-|              | build system                                               |
-+--------------+------------------------------------------------------------+
-
-
-------------
-``<c:make>``
-------------
-
-Executes a Makefile.
-
-Parameters
-----------
-
-+----------------+-----------------------------------------------------------+
-| Name           | Description                                               |
-+================+===========================================================+
-| ``target``     | Name of the target to execute (defaults to "all")         |
-+----------------+-----------------------------------------------------------+
-| ``file``       | Path to the Makefile that should be used.                 |
-+----------------+-----------------------------------------------------------+
-| ``keep-going`` | Whether `make` should try to continue even after          |
-|                | encountering errors.                                      |
-+----------------+-----------------------------------------------------------+
-| ``jobs``       | Number of parallel jobs used by make.                     |
-+----------------+-----------------------------------------------------------+
-| ``directory``  | Path of the directory in which make should be called.     |
-+----------------+-----------------------------------------------------------+
-| ``args``       | Any space separated arguments to pass to the makefile.    | 
-|                | Usually in the form:                                      |
-|                | ``"parameter1=value1 parameter2=value2"``.                |
-+----------------+-----------------------------------------------------------+
-
-
-Examples
---------
-
-.. code-block:: xml
-
-  <c:make target="compile" file="build/Makefile" />
-
-Runs the target "compile" of the ``Makefile`` located in the sub-directory
-``build``.
-
-.. code-block:: xml
-
-  <c:make target="compile" file="build/Makefile" directory="work" args="coverage=1" />
-
-Same as previous but execute the command in the ``work`` directory and call
-the makefile with the command line argument ``coverage=1``.
-
----------------
-``<c:cppunit>``
----------------
-
-Report the test output generated by the CppUnit_ unit testing framework. The
-output from CppUnit must be in XML format and in already, specified by the
-``file`` argument of this recipe.
-
-.. _cppunit: http://cppunit.sourceforge.net
-
-Parameters
-----------
-
-+----------------+-----------------------------------------------------------+
-| Name           | Description                                               |
-+================+===========================================================+
-| ``file``       | Path to the cppunit XML output file.                      |
-+----------------+-----------------------------------------------------------+
-
-Examples
---------
-
-.. code-block:: xml
-
-  <sh:exec executable="run_unit_tests" output="test_results.xml" />
-  <c:cppunit file="test_results.xml" />
-
-Runs the program ``run_unit_tests`` to gather the data output by CppUnit in the
-``test_results.xml`` file and then reports it.
-
-
-Java Tools
-==========
-
-A bundle of recipe commands that support tools commonly used by Java projects.
-
-:Namespace: ``http://bitten.cmlenz.net/tools/java``
-:Common prefix: ``java``
-
-
---------------
-``<java:ant>``
---------------
-
-Runs an Ant_ build.
-
-.. _ant: http://ant.apache.org/
-
-Parameters
-----------
-
-+----------------+-----------------------------------------------------------+
-| Name           | Description                                               |
-+================+===========================================================+
-| ``file``       | Path of the build file, relative to the project source    |
-|                | directory (default is ``build.xml``).                     |
-+----------------+-----------------------------------------------------------+
-| ``target``     | Name of the build target(s) to execute.                   |
-+----------------+-----------------------------------------------------------+
-| ``args``       | Additional arguments to pass to Ant, separated by         |
-|                | whitespace.                                               |
-+----------------+-----------------------------------------------------------+
-| ``keep_going`` | Tell Ant to continue even when errors are in encountered  |
-|                | in the build.                                             |
-+----------------+-----------------------------------------------------------+
-
-Examples
---------
-
-.. code-block:: xml
-
-  <java:ant target="compile" />
-
-Executes the target ``compile`` of the ``build.xml`` buildfile at the top of the
-project source directory.
-
-
---------------------
-``<java:cobertura>``
---------------------
-
-Extract code coverage data from a Cobertura_ XML file.
-
-.. _cobertura: http://cobertura.sourceforge.net/
-
-Parameters
-----------
-
-+----------------+-----------------------------------------------------------+
-| Name           | Description                                               |
-+================+===========================================================+
-| ``file``       | Path to the XML file generated by Cobertura               |
-+----------------+-----------------------------------------------------------+
-
-Examples
---------
-
-.. code-block:: xml
-
-  <java:cobertura file="build/cobertura.xml" />
-
-Reads the specifid XML file, extracts the coverage data, and builds a coverage
-report to be sent to the build master.
-
-
-----------------
-``<java:junit>``
-----------------
-
-Extracts information about unit test results from a file in JUnit_ XML format.
-
-.. _junit: http://junit.org/index.htm
-
-Parameters
-----------
-
-+----------------+-----------------------------------------------------------+
-| Name           | Description                                               |
-+================+===========================================================+
-| ``file``       | Path to the JUnit XML test results file. This can include |
-|                | wildcards, in which case all the file matching the        |
-|                | pattern will be included.                                 |
-+----------------+-----------------------------------------------------------+
-| ``srcdir``     | Path of the directory unit test sources. Used to link the |
-|                | test cases to files.                                      |
-+----------------+-----------------------------------------------------------+
-
-The ``file`` attribute is required.
-
-Examples
---------
-
-.. code-block:: xml
-
-  <java:junit file="build/tests/results/TEST-*.xml" srcdir="src/tests" />
-
-Collects the test results from all files in the `build/tests/results` directory
-that match the pattern `TEST-*.xml`. Also, maps the class names in the results
-files to Java source files in the directory `src/tests`.
-
-
-PHP Tools
-=========
-
-A bundle of recipe commands for PHP_ projects.
-
-:Namespace: ``http://bitten.cmlenz.net/tools/php``
-:Common prefix: ``php``
-
-.. _php: http://php.net/
-
----------------
-``<php:phing>``
----------------
-
-Runs a Phing_ build.
-
-.. _phing: http://phing.info/
-
-Parameters
-----------
-
-+-------------------+-------------------------------------------------------+
-| Name              | Description                                           |
-+===================+=======================================================+
-| ``file``          | Path of the build file, relative to the project       |
-|                   | source directory (default is ``build.xml``).          |
-+-------------------+-------------------------------------------------------+
-| ``target``        | Name of the build target(s) to execute.               |
-+-------------------+-------------------------------------------------------+
-| ``args``          | Additional arguments to pass to Phing, separated by   |
-|                   | whitespace.                                           |
-+-------------------+-------------------------------------------------------+
-| ``executable``    | Phing executable program (default is ``phing``).      |
-+-------------------+-------------------------------------------------------+
-
-
-Examples
---------
-
-.. code-block:: xml
-
-  <php:phing target="compile" />
-
-Executes the target ``compile`` of the ``build.xml`` buildfile at the top of the
-project source directory.
-
-
------------------
-``<php:phpunit>``
------------------
-
-Extracts information from PHPUnit_ test results recorded in an XML file.
-
-.. _phpunit: http://www.phpunit.de/
-
-Parameters
-----------
-
-+----------------+-----------------------------------------------------------+
-| Name           | Description                                               |
-+================+===========================================================+
-| ``file``       | Path to the XML results file, relative to the project     |
-|                | source directory.                                         |
-+----------------+-----------------------------------------------------------+
-
-Examples
---------
-
-.. code-block:: xml
-
-  <php:phpunit file="build/test-results.xml"/>
-
-Extracts the test results from the XML file located at
-``build/test-results.xml``.
-
-
-------------------
-``<php:coverage>``
-------------------
-
-Extracts coverage information Phing_'s code coverage task recorded in an XML
-file.
-
-Parameters
-----------
-
-+---------------+-----------------------------------------------------------+
-| Name          | Description                                               |
-+===============+===========================================================+
-| ``file``      | Path to the XML coverage file, relative to the project    |
-|               | source directory.                                         |
-+---------------+-----------------------------------------------------------+
-
-Examples
---------
-
-.. code-block:: xml
-
-  <php:coverage file="build/coverage.xml" />
-
-
-Python Tools
-============
-
-A bundle of recipe commands that support tools commonly used by Python_
-projects.
-
-:Namespace: ``http://bitten.cmlenz.net/tools/python``
-:Common prefix: ``python``
-
-.. _python: http://www.python.org/
-
-
------------------
-``<python:exec>``
------------------
-
-Executes a Python script.
-
-Parameters
-----------
-
-+----------------+-----------------------------------------------------------+
-| Name           | Description                                               |
-+================+===========================================================+
-| ``file``       | Path of the script to execute, relative to the project    |
-|                | source directory.                                         |
-+----------------+-----------------------------------------------------------+
-| ``module``     | Name of the Python module to execute.                     |
-+----------------+-----------------------------------------------------------+
-| ``function``   | Name of the function in the Python module to run. Only    |
-|                | works when also specifying the `module` attribute.        |
-+----------------+-----------------------------------------------------------+
-| ``args``       | Any arguments that should be passed to the script.        |
-+----------------+-----------------------------------------------------------+
-| ``output``     | Path to a file where any output by the script should be   |
-|                | recorded.                                                 |
-+----------------+-----------------------------------------------------------+
-
-Either `file` or `module` must be specified.
-
-Examples
---------
-
-.. code-block:: xml
-
-  <python:exec module="pylint.lint" output="pylint-report.txt" args="myproj" />
-
-Executes Pylint_ on the module/package ``myproj`` and stores the output into a
-file named ``pylint-report.txt``.
-
-
-----------------------
-``<python:distutils>``
-----------------------
-
-Executes a distutils_ script.
-
-.. _distutils: http://docs.python.org/lib/module-distutils.html
-
-Parameters
-----------
-
-+----------------+-----------------------------------------------------------+
-| Name           | Description                                               |
-+================+===========================================================+
-| `command`      | The name of the `distutils` command that should be run    |
-+----------------+-----------------------------------------------------------+
-| `options`      | Additional options to pass to the command, separated by   |
-|                | spaces                                                    |
-+----------------+-----------------------------------------------------------+
-
-Examples
---------
-
-.. code-block:: xml
-
-  <python:distutils command="sdist" />
-
-Instructs `distutils` to produce a source distribution.
-
-.. code-block:: xml
-
-  <python:distutils command="unittest" options="
-      --xml-output build/test-results.xml
-      --coverage-summary build/test-coverage.txt
-      --coverage-dir build/coverage"/>
-
-Instructs `distutils` to run the ``unittest`` command (which is provided by
-Bitten), and passes the options needed to determine the output paths for test
-results and code coverage reports.
-
-
----------------------
-``<python:unittest>``
----------------------
-
-Extracts information from unittest_ results recorded in an XML file.
-
-.. _unittest: http://docs.python.org/lib/module-unittest.html
-.. note:: This report must be used in conjunction with the ``distutils`` command
-          "unittest" that comes with Bitten.
-
-Parameters
-----------
-
-+----------------+-----------------------------------------------------------+
-| Name           | Description                                               |
-+================+===========================================================+
-| ``file``       | Path to the XML results file, relative to the project     |
-|                | source directory.                                         |
-+----------------+-----------------------------------------------------------+
-
-Examples
---------
-
-.. code-block:: xml
-
-  <python:unittest file="build/test-results.xml"/>
-
-Extracts the test results from the XML file located at
-``build/test-results.xml``.
-
-
-------------------
-``<python:trace>``
-------------------
-
-Extracts coverage information recorded by the built-in Python module
-``trace.py``.
-
-Parameters
-----------
-
-+--------------+-------------------------------------------------------------+
-| Name         | Description                                                 |
-+==============+=============================================================+
-| ``summary``  | Path to the summary file written by ``trace.py``,           |
-|              | relative to the project source directory.                   |
-+--------------+-------------------------------------------------------------+
-| ``coverdir`` | Path to the directory containing the coverage files written |
-|              | by ``trace.py``, relative to the project source directory.  |
-+--------------+-------------------------------------------------------------+
-| ``include``  | List of glob patterns (separated by space) that specify     |
-|              | which Python file should be included in the coverage report |
-+--------------+-------------------------------------------------------------+
-| ``exclude``  | List of glob patterns (separated by space) that specify     |
-|              | which Python file should be excluded from the coverage      |
-|              | report                                                      |
-+--------------+-------------------------------------------------------------+
-
-Examples
---------
-
-.. code-block:: xml
-
-  <python:trace summary="build/trace.out" coverdir="build/coverage" />
-
--------------------
-``<python:pylint>``
--------------------
-
-Extracts information from Pylint_ reports.
-
-.. _pylint: http://www.logilab.org/projects/pylint
-
-Parameters
-----------
-
-+--------------+-------------------------------------------------------------+
-| Name         | Description                                                 |
-+==============+=============================================================+
-| ``file``     | Path to the file containing the Pylint output, relative to  |
-|              | the project source directory.                               |
-+--------------+-------------------------------------------------------------+
-
-Examples
---------
-
-.. code-block:: xml
-
-  <python:pylint file="build/pylint.out" />
-
-
-Subversion Tools
-================
-
-A collection of recipe commands for working with the Subversion_ version
-control system. This commands are commonly used as the first step of a build
-recipe to actually pull the code that should be built from the repository.
-
-.. _subversion: http://subversion.tigris.org/
-
-:Namespace: ``http://bitten.cmlenz.net/tools/svn``
-:Common prefix: ``svn``
-
-
-------------------
-``<svn:checkout>``
-------------------
-
-Check out a working copy from a Subversion repository.
-
-Parameters
-----------
-
-+--------------+-------------------------------------------------------------+
-| Name         | Description                                                 |
-+==============+=============================================================+
-| ``url``      | URL of the repository.                                      |
-+--------------+-------------------------------------------------------------+
-| ``path``     | The path inside the repository that should be checked out.  |
-|              | You should normally set this to ``${path}`` so that the     |
-|              | path of the build configuration is used.                    |
-+--------------+-------------------------------------------------------------+
-| ``revision`` | The revision that should be checked out. You should         |
-|              | normally set this to ``${revision}`` so that the revision   |
-|              | of the build is used.                                       |
-+--------------+-------------------------------------------------------------+
-| ``dir``      | Path specifying which directory the sources should be       |
-|              | checked out to (defaults to '.').                           |
-+--------------+-------------------------------------------------------------+
-| ``verbose``  | Whether to log the list of checked out files (defaults to   |
-|              | False).                                                     |
-+--------------+-------------------------------------------------------------+
-
-
-Examples
---------
-
-.. code-block:: xml
-
-  <svn:checkout url="http://svn.example.org/repos/myproject/"
-      path="${path}" revision="${revision}"/>
-
-This checks out the a working copy into the current directory.
-
-
-----------------
-``<svn:export>``
-----------------
-
-Download a file or directory from a Subversion repository. This is similar to
-performing a checkout, but will not include the meta-data Subversion uses to
-connect the local working copy to the repository (i.e. it does not include the
-``.svn`` directories.)
-
-Parameters
-----------
-
-+--------------+-------------------------------------------------------------+
-| Name         | Description                                                 |
-+==============+=============================================================+
-| ``url``      | URL of the repository.                                      |
-+--------------+-------------------------------------------------------------+
-| ``path``     | The path inside the repository that should be checked out.  |
-|              | You should normally set this to ``${path}`` so that the     |
-|              | path of the build configuration is used.                    |
-+--------------+-------------------------------------------------------------+
-| ``revision`` | The revision that should be checked out. You should         |
-|              | normally set this to ``${revision}`` so that the revision   |
-|              | of the build is used.                                       |
-+--------------+-------------------------------------------------------------+
-| ``dir``      | Path specifying which directory the sources should be       |
-|              | exported to (defaults to '.')                               |
-+--------------+-------------------------------------------------------------+
-
-Examples
---------
-
-.. code-block:: xml
-
-  <svn:export url="http://svn.example.org/repos/myproject/"
-      path="${path}" revision="${revision}"/>
-
-This downloads the file or directory at ``${path}`` from the Subversion
-repository at ``http://svn.example.org/repos/myproject/``. Variables are used
-for the ``path`` and ``revision`` attributes so they are populated from the
-properties of the build and build configuration.
-
-
-----------------
-``<svn:update>``
-----------------
-
-Update an existing working copy from a Subversion repository to a specific
-revision.
-
-Parameters
-----------
-
-+--------------+-------------------------------------------------------------+
-| Name         | Description                                                 |
-+==============+=============================================================+
-| ``revision`` | The revision that should be checked out. You should         |
-|              | normally set this to ``${revision}`` so that the revision   |
-|              | of the build is used.                                       |
-+--------------+-------------------------------------------------------------+
-| ``dir``      | Path specifying the directory containing the sources to be  |
-|              | updated (defaults to '.')                                   |
-+--------------+-------------------------------------------------------------+
-
-Examples
---------
-
-.. code-block:: xml
-
-  <svn:update revision="${revision}"/>
-
-This updates the working copy in the current directory. The revision is
-specified as a variable so that it is populated from the properties of the
-build.
-
-
-XML Tools
-=========
-
-A collection of recipe commands for XML processing.
-
-:Namespace: ``http://bitten.cmlenz.net/tools/xml``
-:Common prefix: ``x``
-
-
------------------
-``<x:transform>``
------------------
-
-Apply an XSLT stylesheet .
-
-.. note:: that this command requires either libxslt_ (with `Python bindings`_)
-          or, on Windows platforms, MSXML (version 3 or later) to be installed
-          on the slave machine.
-
-.. _libxslt: http://xmlsoft.org/XSLT/
-.. _`python bindings`: http://xmlsoft.org/XSLT/python.html
-
-Parameters
-----------
-
-+----------------+-----------------------------------------------------------+
-| Name           | Description                                               |
-+================+===========================================================+
-| ``src``        | Path of the source XML file.                              |
-+----------------+-----------------------------------------------------------+
-| ``dest``       | Path of the destition XML file.                           |
-+----------------+-----------------------------------------------------------+
-| ``stylesheet`` | Path to the XSLT stylesheet file.                         |
-+----------------+-----------------------------------------------------------+
-
-All these are interpreted relative to the project source directory.
-
-Examples
---------
-
-.. code-block:: xml
-
-  <x:transform src="src.xml" dest="dest.xml" stylesheet="util/convert.xsl" />
-
-This applies the stylesheet in ``util/convert.xsl`` to the source file
-``src.xml``, and writes the resulting XML document to ``dest.xml``.
deleted file mode 100644
--- a/trac-0.11/doc/index.txt
+++ /dev/null
@@ -1,25 +0,0 @@
-.. -*- mode: rst; encoding: utf-8 -*-
-
-=======
-Preface
-=======
-
-.. image:: logo.png
-   :width: 538
-   :height: 298
-   :align: center
-   :alt: Bitten
-   :class: logo
-
--------------------------------
-Continuous Integration for Trac
--------------------------------
-
-Bitten is a Python-based framework for collecting various software metrics via
-continuous integration. It builds on Trac to provide an integrated web-based
-user interface.
-
- * `Installation <install.html>`_
- * `Build Recipes <recipes.html>`_
- * `Build Recipe Commands <commands.html>`_
- * `Generated API Documentation <api/index.html>`_
deleted file mode 100644
--- a/trac-0.11/doc/install.txt
+++ /dev/null
@@ -1,121 +0,0 @@
-.. -*- mode: rst; encoding: utf-8 -*-
-
-============
-Installation
-============
-
-.. contents:: Contents
-   :depth: 2
-.. sectnum::
-
-
-Prerequisites
-=============
-
-Bitten is written in Python, so make sure that you have Python installed.
-You'll need Python 2.3 or later. Also, make sure that setuptools_, version 0.6a2
-or later, is installed.
-
-.. _setuptools: http://peak.telecommunity.com/DevCenter/setuptools
-
-If that's taken care of, you just need to download and unpack the Bitten
-distribution, and execute the command::
-
-  $ python setup.py install
-
-from the top of the directory where you unpacked (or checked out) the Bitten
-code. Note that you may need administrator/root privileges for this step, as
-it will by default attempt to install Bitten to the Python site-packages
-directory on your system.
-
-It's also a good idea to run the unit tests at this point, to make sure that
-the code works as expected on your platform::
-
-  $ python setup.py test
-
-
-What's left to do now depends on whether you want to use the build master and
-web interface, or just the build slave. In the latter case, you're already
-done. You might need to install software that the build of your project
-requires, but the Bitten build slave itself doesn't require anything extra.
-
-For the build master and web interface, you'll need to install Trac_ 0.10 or
-later and the TracWebAdmin_ plugin. Please refer to the Trac documentation for
-information on how it is installed.
-
-.. _trac: http://trac.edgewall.org/
-.. _tracwebadmin: http://trac.edgewall.org/wiki/WebAdmin
-
-
-Build Master Configuration
-==========================
-
-Once both Bitten and Trac are installed and working, you'll have to introduce
-Bitten to your Trac project environment. If you don't have a  Trac project
-set up yet, you'll need to do so in order to use Bitten.
-
-If you already have a Trac project environment, the Bitten plugin needs to be
-explicitly enabled in the Trac configuration. This is done by adding it to the
-``[components]`` section in ``/path/to/projenv/conf/trac.ini``:
-
-.. code-block:: ini
-
-  [components]
-  bitten.* = enabled
-
-The Trac web interface should now inform you with an error message that the
-environment needs to be upgraded. To do this, run::
-
-  $ trac-admin /path/to/projenv upgrade
-
-This will create the database tables and directories that Bitten requires.
-You probably also want to grant permissions to someone (such as yourself)
-to manage build configurations, and allow anonymous users to view the
-status and results of builds::
-
-  $ trac-admin /path/to/projenv permission add anonymous BUILD_EXEC
-  $ trac-admin /path/to/projenv permission add anonymous BUILD_VIEW
-  $ trac-admin /path/to/projenv permission add [yourname] BUILD_ADMIN
-
-You should now see an additional tab labeled "Build Status" in the Trac
-navigation bar. This link will take you to the list of build configurations,
-which at this point is of course empty.
-
-To add build configurations, you need to have the TracWebAdmin_ plugin
-installed.
-
-.. warning:: The TracWebAdmin needs to be installed even if you're using Trac
-             0.11 or later, which basically provides a builtin web
-             administration interface. Make sure that you disable the plugin in
-             that case, though (``webadmin.* = disabled``). While somewhat
-             counterintuitive, this process allows the Bitten administration UI
-             to neatly integrate into the new web administration interface added
-             in Trac 0.11.
-
-If both TracWebAdmin_ and Bitten are installed, and you are logged in as a user
-with the required permissions, you should see additional administration pages
-inside the “Admin” area, under a group named “Builds”. These pages allow you to
-set options of the build master, and manage build configurations.
-
-Add a new build configuration and fill out the form. Also, add at least one
-target platform after saving the configuration. Last but not least, you'll have
-to "activate" your new build configuration.
-
-
-Running the Build Slave
-=======================
-
-The build slave can be run on any machine that can connect to the machine
-on which the build master is running. The installation of Bitten should have put
-a `bitten-slave` executable on your path. If the script is not on your path,
-look for it in the `bin` or `scripts` subdirectory of your Python installation.
-
-To get a list of options for the build slave, execute it with the `--help`
-option::
-
-  $ bitten-slave --help
-
-To run the build slave against a Bitten-enabled Trac site installed at 
-http://myproject.example.org/trac, you'd run::
-
-  $ bitten-slave http://myproject.example.org/trac/builds
deleted file mode 100644
index 665402ec268b69d884c17379346cf580f2e6355c..e69de29bb2d1d6434b8b29ae775ad8c2e48c5391
GIT binary patch
literal 0
Hc$@<O00001
deleted file mode 100644
index bb4daf76efb72105cca5ea1e1b97fe70dbde9ad9..e69de29bb2d1d6434b8b29ae775ad8c2e48c5391
GIT binary patch
literal 0
Hc$@<O00001
deleted file mode 100644
index aba3415df5c2aebadd03ccf631cd9549f721648a..e69de29bb2d1d6434b8b29ae775ad8c2e48c5391
GIT binary patch
literal 0
Hc$@<O00001
deleted file mode 100644
--- a/trac-0.11/doc/recipes.txt
+++ /dev/null
@@ -1,68 +0,0 @@
-.. -*- mode: rst; encoding: utf-8 -*-
-
-=============
-Build Recipes
-=============
-
-A build recipe tells a build slave how a project is to be built. It consists of
-multiple build steps, each defining a command to execute, and where artifacts
-can be found after that command has successfully completed.
-
-Build recipes are intended to supplement existing project build files (such as
-Makefiles), not to replace them. In general, a recipe will be much simpler than
-the build file itself, because it doesn't deal with all the details of the
-build. It just automates the execution of the build and lets the build slave
-locate any artifacts and metrics data generated in the course of the build.
-
-A recipe can and should split the build into multiple separate steps so that the
-build slave can provide better status reporting to the build master while the
-build is still in progress. This is important for builds that might take long to
-execute. In addition, build steps help organize the build results for a more
-structured presentation.
-
-.. contents:: Contents
-   :depth: 2
-.. sectnum::
-
-
-File Format
-===========
-
-Build recipes are stored internally in an XML-based format. Recipe documents
-have a single ``<build>`` root element with one or more ``<step>`` child
-elements. The steps are executed in the order they appear in the recipe.
-
-A ``<step>`` element will consist of any number of commands and reports. Most of
-these elements are declared in XML namespaces, where the namespace URI defines
-a collection of related commands.
-
-Commonly, the first step of any build recipe will perform the checkout from the
-repository.
-
-.. code-block:: xml
-
-  <build xmlns:python="http://bitten.cmlenz.net/tools/python"
-         xmlns:svn="http://bitten.cmlenz.net/tools/svn">
-  
-    <step id="checkout" description="Checkout source from repository">
-      <svn:checkout url="http://svn.example.org/repos/foo"
-          path="${path}" revision="${revision}" />
-    </step>
-  
-    <step id="build" description="Compile to byte code">
-      <python:distutils command="build"/>
-    </step>
-  
-    <step id="test" description="Run unit tests">
-      <python:distutils command="unittest"/>
-      <python:unittest file="build/test-results.xml"/>
-      <python:trace summary="build/test-coverage.txt" 
-          coverdir="build/coverage" include="trac*" exclude="*.tests.*"/>
-    </step>
-  
-  </build>
-
-See `Build Recipe Commands`_ for a comprehensive reference of the commands
-available by default.
-
-.. _`build recipe commands`: commands.html
deleted file mode 100644
--- a/trac-0.11/scripts/proxy.py
+++ /dev/null
@@ -1,93 +0,0 @@
-# Based on the proxy module from the Medusa project
-# Used for inspecting the communication between two BEEP peers
-
-import asynchat
-import asyncore
-import socket
-import sys
-
-
-class proxy_server(asyncore.dispatcher):
-    
-    def __init__(self, host, port):
-        asyncore.dispatcher.__init__ (self)
-        self.create_socket(socket.AF_INET, socket.SOCK_STREAM)
-        self.set_reuse_addr()
-        self.there = (host, port)
-        here = ('', port + 1)
-        self.bind(here)
-        self.listen(5)
-
-    def handle_accept(self):
-        proxy_receiver(self, self.accept())
-
-
-class proxy_sender(asynchat.async_chat):
-
-    def __init__(self, receiver, address):
-        asynchat.async_chat.__init__(self)
-        self.receiver = receiver
-        self.set_terminator(None)
-        self.create_socket(socket.AF_INET, socket.SOCK_STREAM)
-        self.buffer = ''
-        self.set_terminator('\r\n')
-        self.connect(address)
-        print 'L:', '<wait for incoming connections>'
-
-    def handle_connect(self):
-        print 'L:', '<open connection>'
-
-    def collect_incoming_data(self, data):
-        self.buffer = self.buffer + data
-
-    def found_terminator(self):
-        data = self.buffer
-        self.buffer = ''
-        for line in data.splitlines():
-            print 'L:', '\x1b[35m' + line + '\x1b[0m'
-        self.receiver.push(data + '\r\n')
-
-    def handle_close(self):
-         self.receiver.close()
-         self.close()
-
-
-class proxy_receiver(asynchat.async_chat):
-
-    channel_counter = 0
-
-    def __init__(self, server, (conn, addr)):
-        asynchat.async_chat.__init__(self, conn)
-        self.set_terminator('\r\n')
-        self.server = server
-        self.id = self.channel_counter
-        self.channel_counter = self.channel_counter + 1
-        self.sender = proxy_sender (self, server.there)
-        self.sender.id = self.id
-        self.buffer = ''
-
-    def collect_incoming_data (self, data):
-        self.buffer = self.buffer + data
-
-    def found_terminator(self):
-        data = self.buffer
-        self.buffer = ''
-        for line in data.splitlines():
-            print 'I:', '\x1b[34m' + line + '\x1b[0m'
-        self.sender.push (data + '\r\n')
-
-    def handle_connect(self):
-        print 'I:', '<open connection>'
-
-    def handle_close(self):
-         print 'I:', '<close connection>'
-         self.sender.close()
-         self.close()
-
-
-if __name__ == '__main__':
-    if len(sys.argv) < 3:
-        print 'Usage: %s <server-host> <server-port>' % sys.argv[0]
-    else:
-        ps = proxy_server(sys.argv[1], int(sys.argv[2]))
-        asyncore.loop()
deleted file mode 100644
--- a/trac-0.11/setup.cfg
+++ /dev/null
@@ -1,8 +0,0 @@
-[egg_info]
-tag_build = dev
-tag_svn_revision = true
-
-[unittest]
-xml_output = build/test-results.xml
-coverage_summary = build/test-coverage.txt
-coverage_dir = build/coverage
deleted file mode 100755
--- a/trac-0.11/setup.py
+++ /dev/null
@@ -1,89 +0,0 @@
-#!/usr/bin/env python
-# -*- coding: utf-8 -*-
-#
-# Copyright (C) 2007 Edgewall Software
-# Copyright (C) 2005-2007 Christopher Lenz <cmlenz@gmx.de>
-# 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 os
-from setuptools import setup, find_packages
-import sys
-
-sys.path.append(os.path.join('doc', 'common'))
-try:
-    from doctools import build_doc, test_doc
-except ImportError:
-    build_doc = test_doc = None
-
-NS = 'http://bitten.cmlenz.net/tools/'
-
-setup(
-    name = 'Bitten',
-    version = '0.6',
-    description = 'Continuous integration for Trac',
-    long_description = \
-"""A Trac plugin for collecting software metrics via continuous integration.""",
-    author = 'Edgewall Software',
-    author_email = 'info@edgewall.org',
-    license = 'BSD',
-    url = 'http://bitten.edgewall.org/',
-    download_url = 'http://bitten.edgewall.org/wiki/Download',
-    zip_safe = False,
-
-    packages = find_packages(exclude=['*.tests*']),
-    package_data = {
-        'bitten': ['htdocs/*.*',
-                   'htdocs/charts_library/*.swf',
-                   'templates/*.html']
-    },
-    test_suite = 'bitten.tests.suite',
-    entry_points = {
-        'console_scripts': [
-            'bitten-slave = bitten.slave:main'
-        ],
-        'distutils.commands': [
-            'unittest = bitten.util.testrunner:unittest'
-        ],
-        'trac.plugins': [
-            'bitten.admin = bitten.admin',
-            'bitten.main = bitten.main',
-            'bitten.master = bitten.master',
-            'bitten.web_ui = bitten.web_ui',
-            'bitten.testing = bitten.report.testing',
-            'bitten.coverage = bitten.report.coverage'
-        ],
-        'bitten.recipe_commands': [
-            NS + 'sh#exec = bitten.build.shtools:exec_',
-            NS + 'sh#pipe = bitten.build.shtools:pipe',
-            NS + 'c#configure = bitten.build.ctools:configure',
-            NS + 'c#autoreconf = bitten.build.ctools:autoreconf',
-            NS + 'c#cppunit = bitten.build.ctools:cppunit',
-            NS + 'c#cunit = bitten.build.ctools:cunit',
-            NS + 'c#gcov = bitten.build.ctools:gcov',
-            NS + 'c#make = bitten.build.ctools:make',
-            NS + 'java#ant = bitten.build.javatools:ant',
-            NS + 'java#junit = bitten.build.javatools:junit',
-            NS + 'java#cobertura = bitten.build.javatools:cobertura',
-            NS + 'php#phing = bitten.build.phptools:phing',
-            NS + 'php#phpunit = bitten.build.phptools:phpunit',
-            NS + 'php#coverage = bitten.build.phptools:coverage',
-            NS + 'python#coverage = bitten.build.pythontools:coverage',
-            NS + 'python#distutils = bitten.build.pythontools:distutils',
-            NS + 'python#exec = bitten.build.pythontools:exec_',
-            NS + 'python#figleaf = bitten.build.pythontools:figleaf',
-            NS + 'python#pylint = bitten.build.pythontools:pylint',
-            NS + 'python#trace = bitten.build.pythontools:trace',
-            NS + 'python#unittest = bitten.build.pythontools:unittest',
-            NS + 'svn#checkout = bitten.build.svntools:checkout',
-            NS + 'svn#export = bitten.build.svntools:export',
-            NS + 'svn#update = bitten.build.svntools:update',
-            NS + 'xml#transform = bitten.build.xmltools:transform'
-        ]
-    },
-
-    cmdclass = {'build_doc': build_doc, 'test_doc': test_doc}
-)
Copyright (C) 2012-2017 Edgewall Software