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_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, 'recipe': 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, 'recipe': False }, configs[1])
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])
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 setUp(self): self.env = EnvironmentStub(enable=['trac.*', 'bitten.*']) 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() # Hook up a dummy repository self.repos = Mock() self.env.get_repository = lambda authname=None: self.repos # 0.11 try: # 0.12+ from trac.core import Component, implements from trac.versioncontrol.api import IRepositoryConnector, \ IRepositoryProvider class DummyRepos(Component): implements(IRepositoryConnector, IRepositoryProvider) def get_supported_types(self): yield ('dummy', 9) def get_repository(this, repos_type, repos_dir, params): return self.repos # Note: 'this' vs 'self' usage def get_repositories(self): yield ('', {'dir': 'dummy_dir', 'type': 'dummy'}) self.dummy = DummyRepos except ImportError: self.dummy = None # not supported, will use get_repository()
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_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', chrome={'warnings': []}, href=Href('/'), args={ 'save': '', 'name': 'foo', 'path': 'invalid/path' }) def get_node(path, rev=None): raise TracError('No such node') self.repos.get_node = get_node provider = BuildConfigurationsAdminPageProvider(self.env) provider.render_admin_panel(req, 'bitten', 'configs', 'foo') self.failUnless(req.chrome['warnings'], "No warnings?") self.assertEquals(req.chrome['warnings'], [ 'Invalid Repository Path: "invalid/path" does not exist ' 'within the "(default)" repository.' ])
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 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_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)
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)
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)
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, normalize_rev=lambda rev: rev, youngest_rev=123) self.repos.authz = Mock(has_permission=lambda path: True, assert_permission=lambda path: None) 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'])
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())
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.get_node=lambda path, rev=None: root self.repos.youngest_rev=123 self.repos.get_changeset=lambda rev: Mock(author='joe', date=99) 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'] self.assertEquals(Resource('build', 'test'), data['context'].resource) self.assertEquals([], data['config']['attachments']['attachments']) self.assertEquals('/trac/attachment/build/test/', data['config']['attachments']['attach_href'])
def test_bitten_keeps_order_of_revisions_from_versioncontrol(self): # Trac's API specifies that they are sorted chronological (backwards) # We must not assume that these revision numbers can be sorted later on, # for example the mercurial plugin will return the revisions as strings # (e.g. '880:4c19fa95fb9e') 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/'+config.name, href=Href('/trac'), args={}, chrome={}, authname='joe', perm=PermissionCache(self.env, 'joe')) # revisions are intentionally not sorted in any way - bitten should just keep them! revision_ids = [5, 8, 2] revision_list = [('trunk', revision, 'edit') for revision in revision_ids] root = Mock(get_entries=lambda: ['foo'], get_history=lambda: revision_list) self.repos.get_node=lambda path, rev=None: root self.repos.youngest_rev=5 self.repos.get_changeset=lambda rev: Mock(author='joe', date=99) module = BuildConfigController(self.env) assert module.match_request(req) _, data, _ = module.process_request(req) actual_revision_ids = data['config']['revisions'] self.assertEquals(revision_ids, actual_revision_ids)
def test_process_new_platform_no_name(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': '', 'platform_name': '' }) provider = BuildConfigurationsAdminPageProvider(self.env) try: provider.render_admin_panel(req, 'bitten', 'configs', 'foo') self.fail("No TracError?") except Exception, e: self.assertEquals(e.message, 'Missing required field "name"') self.assertEquals(e.title, 'Missing field')
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', chrome={'warnings': []}, href=Href('/'), args={ 'save': '', 'name': 'foo', 'recipe': '<build><step /></build>' }) provider = BuildConfigurationsAdminPageProvider(self.env) provider.render_admin_panel(req, 'bitten', 'configs', 'foo') self.failUnless(req.chrome['warnings'], "No warnings?") self.assertEquals( req.chrome['warnings'], ['Invalid Recipe: Steps must have an "id" attribute.'])
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 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_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_populate_thread_race_condition(self): messages = [] self.env.log = Mock(info=lambda msg, *args: messages.append(msg)) def get_history(): yield ('somepath', 123, 'edit') yield ('somepath', 121, 'edit') yield ('somepath', 120, 'edit') time.sleep(1) # sleep to make sure both threads collect 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=get_history), 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() def build_populator(): queue = BuildQueue(self.env, build_all=True) queue.populate() thread1 = threading.Thread(target=build_populator) thread2 = threading.Thread(target=build_populator) thread1.start() thread2.start() thread1.join() thread2.join() # check builds got added 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) # check attempts at duplicate inserts were logged. failure_messages = [ x for x in messages if x.startswith('Failed to insert build') ] self.assertEqual(6, len(failure_messages))
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_case_insensitive(self): BuildConfig(self.env, 'test', active=True).insert() platform = TargetPlatform(self.env, config='test', name="Unix") platform.rules.append(('os', 'LiNUX')) platform.insert() platform_id = platform.id queue = BuildQueue(self.env) platforms = queue.match_slave('foo', {'os': 'linux'}) self.assertEqual(1, len(platforms)) self.assertEqual(platform_id, platforms[0].id)
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_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
def _create_config(self, req): req.perm.assert_permission('BUILD_CREATE') config = BuildConfig(self.env) warnings = self._update_config(req, config) if warnings: if len(warnings) == 1: raise TracError(warnings[0], 'Add Configuration') else: raise TracError('Errors: %s' % ' '.join(warnings), 'Add Configuration') return config
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_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])
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=to_datetime(42, utc)), 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" version="%d"> <platform>Power Macintosh</platform> <os family="posix" version="8.1.0">Darwin</os> <package name="java" version="2.4.3"/> </slave>""" % PROTOCOL_VERSION) 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, incookie=Cookie('trac_auth=')) module = BuildMaster(self.env) assert module.match_request(req) self.assertRaises(RequestDone, module.process_request, req) 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)