Example #1
0
    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)
Example #2
0
    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)

        # posix
        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)
        # windows
        output = formatter(step, None, None, u'error in foo\\win.c:123: bad')
        self.assertEqual(Markup, type(output))
        self.assertEqual(r'error in <a href="/trac/browser/trunk/foo/win.c#L123">'
                         'foo\win.c:123</a>: bad', output)
Example #3
0
    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)
Example #4
0
    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()

        outheaders = {}
        outbody = StringIO()
        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'),
                   send_response=lambda x: outheaders.setdefault('Status', x),
                   send_header=lambda x, y: outheaders.setdefault(x, y),
                   write=outbody.write,
                   incookie=Cookie('trac_auth='))

        module = BuildMaster(self.env)

        module._start_new_step(build, 'foo').insert()

        assert module.match_request(req)

        self.assertRaises(RequestDone, module.process_request, req)

        self.assertEqual(405, outheaders['Status'])
        self.assertEqual('Method GET not allowed', outbody.getvalue())
Example #5
0
    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()
        assert repos, 'No "(default)" Repository: Add a repository or alias ' \
                      'named "(default)" to Trac.'

        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 = to_timestamp(repos.get_changeset(rev).date)
                    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:
            try:
                build.insert(db=db)
                db.commit()
            except Exception, e:
                # really only want to catch IntegrityErrors raised when
                # a second slave attempts to add builds with the same
                # (config, platform, rev) as an existing build.
                self.log.info('Failed to insert build of configuration "%s" '
                    'at revision [%s] on platform [%s]: %s',
                    build.config, build.rev, build.platform, e)
                db.rollback()
Example #6
0
    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)

        # posix
        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)
        # windows
        output = formatter(step, None, None, u'error in foo\\win.c:123: bad')
        self.assertEqual(Markup, type(output))
        self.assertEqual(
            r'error in <a href="/trac/browser/trunk/foo/win.c#L123">'
            'foo\win.c:123</a>: bad', output)
Example #7
0
    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: '*****@*****.**'
        })
        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: '*****@*****.**'
        }
        for propname, propvalue in cursor:
            self.assertEqual(expected[propname], propvalue)
Example #8
0
    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)
Example #9
0
    def test_view_build(self):
        config = BuildConfig(self.env, name='test', path='trunk')
        config.insert()
        platform = TargetPlatform(self.env, config='test', name='any')
        platform.insert()
        build = Build(self.env, config='test', platform=1, rev=123, rev_time=42,
                      status=Build.SUCCESS, slave='hal')
        build.insert()

        PermissionSystem(self.env).grant_permission('joe', 'BUILD_VIEW')
        req = Mock(method='GET', base_path='', cgi_location='',
                   path_info='/build/test/1', 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.get_node=lambda path, rev=None: root
        self.repos.get_changeset=lambda rev: Mock(author='joe')

        module = BuildController(self.env)
        assert module.match_request(req)
        _, data, _ = module.process_request(req)

        self.assertEqual('view_build', data['page_mode'])

        self.assertEquals(Resource('build', 'test/1'), data['context'].resource)

        self.assertEquals([], data['build']['attachments']['attachments'])
        self.assertEquals('/trac/attachment/build/test/1/',
                                data['build']['attachments']['attach_href'])
Example #10
0
    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,
                   incookie=Cookie('trac_auth='))

        module = BuildMaster(self.env)
        assert module.match_request(req)

        self.assertRaises(RequestDone, module.process_request, req)

        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
Example #11
0
    def test_format_bad_links(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'Linking -I../.. with ../libtool')
        self.assertEqual(Markup, type(output))
        self.assertEqual('Linking -I../.. with ../libtool', output)
Example #12
0
    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>""")
        outheaders = {}
        outbody = StringIO()
        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,
                   send_response=lambda x: outheaders.setdefault('Status', x),
                   send_header=lambda x, y: outheaders.setdefault(x, y),
                   write=outbody.write,
                   incookie=Cookie('trac_auth='))

        module = BuildMaster(self.env)

        module._start_new_step(build, 'foo').insert()

        assert module.match_request(req)

        self.assertRaises(RequestDone, module.process_request, req)

        self.assertEquals(400, outheaders['Status'])
        self.assertEquals('XML parser error', outbody.getvalue())
Example #13
0
    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)
Example #14
0
    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()

        outheaders = {}
        outbody = StringIO()
        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'),
                   send_response=lambda x: outheaders.setdefault('Status', x),
                   send_header=lambda x, y: outheaders.setdefault(x, y),
                   write=outbody.write,
                   incookie=Cookie('trac_auth='))

        module = BuildMaster(self.env)
        assert module.match_request(req)

        self.assertRaises(RequestDone, module.process_request, req)

        self.assertEqual(404, outheaders['Status'])
        self.assertEqual("No such collection 'files'", outbody.getvalue())
Example #15
0
    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])
Example #16
0
    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.
        """
        db = self.env.get_db_cnx()
        builds = []

        for config in BuildConfig.select(self.env, db=db):
            platforms = []
            for platform, rev, build in collect_changes(config, db=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)
                    _repos_name, repos, _repos_path = get_repos(
                                    self.env, config.path, None)

                    rev_time = to_timestamp(repos.get_changeset(rev).date)
                    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:
            try:
                build.insert(db=db)
                db.commit()
            except Exception, e:
                # really only want to catch IntegrityErrors raised when
                # a second slave attempts to add builds with the same
                # (config, platform, rev) as an existing build.
                self.log.info('Failed to insert build of configuration "%s" '
                    'at revision [%s] on platform [%s]: %s',
                    build.config, build.rev, build.platform, e)
                db.rollback()
Example #17
0
    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()
Example #18
0
    def test_initiate_build(self):
        config = BuildConfig(self.env,
                             'test',
                             path='somepath',
                             active=True,
                             recipe='<build><step id="s1"></step></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={},
                   authname='hal',
                   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,
                   form_token="12345",
                   incookie=Cookie('trac_auth='))

        module = BuildMaster(self.env)
        assert module.match_request(req)

        self.assertRaises(RequestDone, module.process_request, req)
        self.assertEqual(200, outheaders['Status'])
        self.assertEqual('163', 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" form_token="12345" '
            'name="hal" path="somepath" platform="Unix" '
            'reponame="" repopath="somepath" '
            'revision="123"><step id="s1"/></build>', outbody.getvalue())

        # Make sure the started timestamp has been set
        build = Build.fetch(self.env, build.id)
        assert build.started
Example #19
0
    def test_delete_platform_with_pending_builds(self):
        """Check that deleting a platform with pending builds removes those pending builds"""
        db = self.env.get_db_cnx()
        platform = TargetPlatform(self.env, config='test', name='Linux')
        platform.insert()
        build = Build(self.env, config='test', platform=platform.id, rev='42', rev_time=12039)
        build.insert()

        platform.delete()
        pending = list(build.select(self.env, config='test', status=Build.PENDING))
        self.assertEqual(0, len(pending))
Example #20
0
    def test_should_delete_build_platform_dont_exist(self):
        messages = []
        self.env.log = Mock(info=lambda msg, *args: messages.append(msg))
        config = BuildConfig(self.env, 'test', active=True)
        config.insert()
        build = Build(self.env, config=config.name, rev=42,
                        platform="no-stuff", rev_time=123456)
        build.insert()
        queue = BuildQueue(self.env, build_all=True)

        self.assertEqual(True, queue.should_delete_build(build, self.repos))
        self.assert_("platform no longer exists" in messages[0])
Example #21
0
    def test_should_delete_build_platform_dont_exist(self):
        messages = []
        self.env.log = Mock(info=lambda msg, *args: messages.append(msg))
        config = BuildConfig(self.env, 'test', active=True)
        config.insert()
        build = Build(self.env, config=config.name, rev=42,
                        platform="no-stuff", rev_time=123456)
        build.insert()
        queue = BuildQueue(self.env, build_all=True)

        self.assertEqual(True, queue.should_delete_build(build, self.repos))
        self.assert_("platform no longer exists" in messages[0])
Example #22
0
    def test_process_build_step_failure_continue(self):
        recipe = """<build>
  <step id="foo" onerror="continue">
  </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.TOKEN] = '123';

        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,
                   incookie=Cookie('trac_auth=123'))
        module = BuildMaster(self.env)

        module._start_new_step(build, 'foo').insert()

        assert module.match_request(req)

        self.assertRaises(RequestDone, module.process_request, req)

        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)
Example #23
0
    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)
Example #24
0
    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)
Example #25
0
    def test_should_delete_build_outside_revision_range(self):
        messages = []
        self.env.log = Mock(info=lambda msg, *args: messages.append(msg))
        self.repos.rev_older_than = lambda rev1, rev2: rev1 < rev2
        config = BuildConfig(self.env,
                             'test',
                             active=True,
                             min_rev=120,
                             max_rev=123)
        config.insert()
        platform = TargetPlatform(self.env, config='test', name='stuff')
        platform.insert()
        build1 = Build(self.env,
                       config=config.name,
                       rev=42,
                       platform=platform.id,
                       rev_time=123456)
        build1.insert()
        build2 = Build(self.env,
                       config=config.name,
                       rev=10042,
                       platform=platform.id,
                       rev_time=123456)
        build2.insert()
        queue = BuildQueue(self.env, build_all=True)

        self.assertEqual(True, queue.should_delete_build(build1, self.repos))
        self.assertEqual(True, queue.should_delete_build(build2, self.repos))
        self.assert_("outside of the revision range" in messages[0])
        self.assert_("outside of the revision range" in messages[1])
Example #26
0
    def test_should_delete_build_config_deactivated(self):
        messages = []
        self.env.log = Mock(info=lambda msg, *args: messages.append(msg))
        config = BuildConfig(self.env, 'test', active=False)
        config.insert()
        platform = TargetPlatform(self.env, config='test', name='stuff')
        platform.insert()
        build = Build(self.env, config=config.name, rev=42,
                        platform=platform.id, rev_time=123456)
        build.insert()
        queue = BuildQueue(self.env, build_all=True)

        self.assertEqual(True, queue.should_delete_build(build, self.repos))
        self.assert_("configuration is deactivated" in messages[0])
Example #27
0
    def test_should_delete_build_config_none(self):
        out = []
        self.env.log = Mock(
                        info=lambda msg, *args: out.extend([msg] + list(args)))
        platform = TargetPlatform(self.env, config='test', name='stuff')
        platform.insert()
        build = Build(self.env, config='does_not_exist', rev=42,
                        platform=platform.id, rev_time=123456)
        build.insert()
        queue = BuildQueue(self.env, build_all=True)

        self.assertEqual(True, queue.should_delete_build(build, self.repos))
        self.assertTrue("configuration is deactivated" in out[0])
        self.assertEquals('unknown config "does_not_exist"', out[1])
Example #28
0
    def test_should_delete_build_config_deactivated(self):
        messages = []
        self.env.log = Mock(info=lambda msg, *args: messages.append(msg))
        config = BuildConfig(self.env, 'test', active=False)
        config.insert()
        platform = TargetPlatform(self.env, config='test', name='stuff')
        platform.insert()
        build = Build(self.env, config=config.name, rev=42,
                        platform=platform.id, rev_time=123456)
        build.insert()
        queue = BuildQueue(self.env, build_all=True)

        self.assertEqual(True, queue.should_delete_build(build, self.repos))
        self.assert_("configuration is deactivated" in messages[0])
Example #29
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.TOKEN] = '123';
        build.insert()

        inbody = StringIO("""<result step="foo" status="success"
                                     time="2007-04-01T15:30:00.0000"
                                     duration="3.45">
    <log generator="http://bitten.edgewall.org/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,
                   incookie=Cookie('trac_auth='))
        module = BuildMaster(self.env)

        module._start_new_step(build, 'foo').insert()

        assert module.match_request(req)

        self.assertRaises(RequestDone, module.process_request, req)

        self.assertEqual(409, outheaders['Status'])
        self.assertEqual('Token mismatch (wrong slave): slave=, build=123',
                        outbody.getvalue())

        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))
Example #30
0
    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)
Example #31
0
    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)
Example #32
0
    def test_should_delete_build_config_none(self):
        out = []
        self.env.log = Mock(
                        info=lambda msg, *args: out.extend([msg] + list(args)))
        platform = TargetPlatform(self.env, config='test', name='stuff')
        platform.insert()
        build = Build(self.env, config='does_not_exist', rev=42,
                        platform=platform.id, rev_time=123456)
        build.insert()
        queue = BuildQueue(self.env, build_all=True)

        self.assertEqual(True, queue.should_delete_build(build, self.repos))
        self.assertTrue("configuration is deactivated" in out[0])
        self.assertEquals('unknown config "does_not_exist"', out[1])
Example #33
0
    def test_process_attach_build(self):
        body, content_type = encode_multipart_formdata({
                'description': 'baz baz',
                'file': ('baz.txt', 'hello baz'),
                '__FORM_TOKEN': '123456'})
        args = {}
        for k, v in dict(cgi.FieldStorage(fp=StringIO(body), environ={
                    'REQUEST_METHOD': 'POST',
                    'CONTENT_TYPE': content_type})
                    ).items():
            if v.filename:
                args[k] = v
            else:
                args[k] = v.value
        args.update({'collection': 'attach', 'member': 'build'})
        self.assertTrue('file' in args)

        outheaders = {}
        outbody = StringIO()

        req = Mock(args=args, form_token='123456', authname='hal',
                remote_addr='127.0.0.1',
                send_response=lambda x: outheaders.setdefault('Status', x),
                send_header=lambda x, y: outheaders.setdefault(x, y),
                write=outbody.write)

        config = BuildConfig(self.env, 'test', path='somepath', active=True,
                    recipe='')
        config.insert()
        build = Build(self.env, 'test', '123', 1, slave='hal', rev_time=42,
                      started=42, status=Build.IN_PROGRESS)
        build.insert()

        module = BuildMaster(self.env)

        self.assertRaises(RequestDone, module._process_attachment,
                                                req, config, build)
        self.assertEqual(201, outheaders['Status'])
        self.assertEqual('18', outheaders['Content-Length'])
        self.assertEqual('text/plain', outheaders['Content-Type'])
        self.assertEqual('Attachment created', outbody.getvalue())

        build_atts = list(Attachment.select(self.env, 'build', 'test/1'))
        self.assertEquals(1, len(build_atts))
        self.assertEquals('hal', build_atts[0].author)
        self.assertEquals('baz baz', build_atts[0].description)
        self.assertEquals('baz.txt', build_atts[0].filename)
        self.assertEquals('hello baz',
                        build_atts[0].open().read())
Example #34
0
    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
Example #35
0
    def test_populate_not_build_all(self):
        self.env.get_repository = lambda authname=None: Mock(
            get_changeset=lambda rev: Mock(date=to_datetime(rev * 1000, utc)),
            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)
Example #36
0
 def test_insert_invalid_status(self):
     build = Build(self.env,
                   config='test',
                   rev='42',
                   rev_time=12039,
                   status='DUNNO')
     self.assertRaises(AssertionError, build.insert)
Example #37
0
    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()
Example #38
0
    def test_populate_not_build_all(self):
        self.env.get_repository = lambda authname=None: Mock(
            get_changeset=lambda rev: Mock(date=to_datetime(rev * 1000, utc)),
            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)
Example #39
0
    def should_delete_build(self, build, repos):
        config = BuildConfig.fetch(self.env, build.config)

        platform = TargetPlatform.fetch(self.env, build.platform)
        # Platform may or may not exist anymore - get safe name for logging
        platform_name = platform and platform.name \
                        or 'unknown platform "%s"' % build.platform

        # Drop build if platform no longer exists
        if not platform:
            self.log.info('Dropping build of configuration "%s" at '
                     'revision [%s] on %s because the platform no longer '
                     'exists', config.name, build.rev, platform_name)
            return True

        # Ignore pending builds for deactived build configs
        if not config.active:
            self.log.info('Dropping build of configuration "%s" at '
                     'revision [%s] on %s because the configuration is '
                     'deactivated', config.name, build.rev, platform_name)
            return True

        # If not 'build_all', drop if a more recent revision is available
        if not self.build_all and \
                len(list(Build.select(self.env, config=build.config,
                min_rev_time=build.rev_time, platform=build.platform))) > 1:
            self.log.info('Dropping build of configuration "%s" at revision [%s] '
                     'on "%s" because a more recent build exists',
                         config.name, build.rev, platform_name)
            return True

        return False
Example #40
0
    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)
Example #41
0
    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')
Example #42
0
    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()
Example #43
0
    def should_delete_build(self, build, repos):
        config = BuildConfig.fetch(self.env, build.config)

        platform = TargetPlatform.fetch(self.env, build.platform)
        # Platform may or may not exist anymore - get safe name for logging
        platform_name = platform and platform.name \
                        or 'unknown platform "%s"' % build.platform

        # Drop build if platform no longer exists
        if not platform:
            self.log.info(
                'Dropping build of configuration "%s" at '
                'revision [%s] on %s because the platform no longer '
                'exists', config.name, build.rev, platform_name)
            return True

        # Ignore pending builds for deactived build configs
        if not config.active:
            self.log.info(
                'Dropping build of configuration "%s" at '
                'revision [%s] on %s because the configuration is '
                'deactivated', config.name, build.rev, platform_name)
            return True

        # If not 'build_all', drop if a more recent revision is available
        if not self.build_all and \
                len(list(Build.select(self.env, config=build.config,
                min_rev_time=build.rev_time, platform=build.platform))) > 1:
            self.log.info(
                'Dropping build of configuration "%s" at revision [%s] '
                'on "%s" because a more recent build exists', config.name,
                build.rev, platform_name)
            return True

        return False
Example #44
0
    def get_annotation_data(self, context):
        add_stylesheet(context.req, 'bitten/bitten_coverage.css')

        resource = context.resource
        self.log.debug("Looking for coverage report for %s@%s..." % (
                        resource.id, str(resource.version)))
        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.branch):
                continue
            reports = Report.select(self.env, build=build.id,
                                    category='coverage')
            branch_in_config = resource.id[len(config.branch):]
            for report in reports:
                for item in report.items:
                    if item.get('file') == branch_in_config:
                        coverage = item.get('line_hits', '').split()
                        if coverage:
                            # Return first result with line data
                            self.log.debug(
                                "Coverage annotate for %s@%s: %s" % \
                                (resource.id, resource.version, coverage))
                            return coverage
        return []
Example #45
0
 def getBuilds(self,
               req,
               config=None,
               rev=None,
               platform=None,
               slave=None,
               status=None):
     """Retrieve a list of builds that match the selected criteria, with all build properties
     """
     builds = []
     for build in Build.select(self.env,
                               config=config or None,
                               rev=rev or None,
                               platform=platform or None,
                               slave=slave or None,
                               status=status or None):
         if not build:
             continue
         builds.append({
             'id': build.id,
             'config': build.config,
             'rev': build.rev,
             'platform': build.platform,
             'slave': build.slave,
             'started': build.started,
             'stopped': build.stopped,
             'last_activity': build.last_activity,
             'rev_time': build.rev_time,
             'status': build.status,
         })
     return builds
Example #46
0
    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)
Example #47
0
    def get_annotation_data(self, context):
        """add annotation data for lint"""

        context.perm.require('BUILD_VIEW')

        add_stylesheet(context.req, 'bitten/bitten_coverage.css')
        add_stylesheet(context.req, 'bitten/bitten_lintannotator.css')

        resource = context.resource

        # attempt to use the version passed in with the request,
        # otherwise fall back to the latest version of this file.
        try:
            version = context.req.args['rev']
        except (KeyError, TypeError):
            version = resource.version
            self.log.debug('no version passed to get_annotation_data')

        builds = Build.select(self.env, rev=version)

        self.log.debug("Looking for lint report for %s@%s [%s]..." %
                       (resource.id, str(resource.version), version))

        self.itemid = 0
        data = {}
        reports = None
        for build in builds:
            config = BuildConfig.fetch(self.env, build.config)
            if not resource.id.lstrip('/').startswith(config.path.lstrip('/')):
                self.log.debug('Skip build %s' % build)
                continue
            path_in_config = resource.id[len(config.path) + 1:].lstrip('/')
            reports = Report.select(self.env, build=build.id, category='lint')
            for report in reports:
                for item in report.items:
                    if item.get('file') == path_in_config:
                        line = item.get('line')
                        if line:
                            problem = {
                                'category': item.get('category', ''),
                                'tag': item.get('tag', ''),
                                'bid': build.id,
                                'rbuild': report.build,
                                'rstep': report.step,
                                'rid': report.id
                            }
                            data.setdefault(int(line), []).append(problem)
        if data:
            self.log.debug("Lint annotate for %s@%s: %s results" % \
                (resource.id, resource.version, len(data)))
            return data
        if not builds:
            self.log.debug("No builds found")
        elif not reports:
            self.log.debug("No reports found")
        else:
            self.log.debug("No item of any report matched (%s)" % reports)
        return None
Example #48
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))
Example #49
0
    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>""")
        outheaders = {}
        outbody = StringIO()
        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,
                   send_response=lambda x: outheaders.setdefault('Status', x),
                   send_header=lambda x, y: outheaders.setdefault(x, y),
                   write=outbody.write,
                   incookie=Cookie('trac_auth='))

        module = BuildMaster(self.env)

        module._start_new_step(build, 'foo').insert()

        assert module.match_request(req)

        self.assertRaises(RequestDone, module.process_request, req)

        self.assertEquals(400, outheaders['Status'])
        self.assertEquals('XML parser error', outbody.getvalue())
Example #50
0
    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: '*****@*****.**'})
        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: '*****@*****.**'}
        for propname, propvalue in cursor:
            self.assertEqual(expected[propname], propvalue)
Example #51
0
    def test_format_bad_links(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'Linking -I../.. with ../libtool')
        self.assertEqual(Markup, type(output))
        self.assertEqual('Linking -I../.. with ../libtool', output)
Example #52
0
    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)
Example #53
0
 def deleteBuild(self, req, id_):
     """Delete a build referenced from its unique ID
     """
     try:
         build = Build.fetch(self.env, id_)
         build.delete()
         return True
     except Exception:
         return False
Example #54
0
 def resource_exists(self, resource):
     config_name, build_id = self._parse_resource(resource.id)
     if build_id:
         build = Build.fetch(self.env, build_id)
         return build and build.exists or False
     elif config_name:
         config = BuildConfig.fetch(self.env, config_name)
         return config and config.exists or False
     return False
Example #55
0
    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')
        data = {'title': 'Build %s - %s' % (build_id,
                                            _status_title[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)
        repos.authz.assert_permission(config.path)
        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
Example #56
0
 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()
Example #57
0
    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,
                   incookie=Cookie('trac_auth='))

        module = BuildMaster(self.env)
        assert module.match_request(req)

        self.assertRaises(RequestDone, module.process_request, req)

        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