cmlenz@410: # -*- coding: utf-8 -*- cmlenz@410: # cmlenz@410: # Copyright (C) 2005-2007 Christopher Lenz osimons@833: # Copyright (C) 2007-2010 Edgewall Software cmlenz@410: # All rights reserved. cmlenz@410: # cmlenz@410: # This software is licensed as described in the file COPYING, which cmlenz@410: # you should have received as part of this distribution. The terms cmlenz@410: # are also available at http://bitten.edgewall.org/wiki/License. cmlenz@410: cmlenz@410: import shutil cmlenz@410: import tempfile cmlenz@410: import unittest cmlenz@410: cmlenz@439: from trac.core import TracError cmlenz@410: from trac.db import DatabaseManager cmlenz@410: from trac.perm import PermissionCache, PermissionSystem cmlenz@410: from trac.test import EnvironmentStub, Mock cmlenz@439: from trac.util.html import Markup osimons@575: from trac.web.api import HTTPNotFound cmlenz@410: from trac.web.href import Href cmlenz@439: from bitten.main import BuildSystem cmlenz@439: from bitten.model import Build, BuildConfig, BuildStep, TargetPlatform, schema osimons@575: from bitten.web_ui import BuildConfigController, BuildController, \ osimons@575: SourceFileLinkFormatter cmlenz@410: cmlenz@410: osimons@575: class AbstractWebUITestCase(unittest.TestCase): cmlenz@410: cmlenz@410: def setUp(self): cmlenz@458: self.env = EnvironmentStub(enable=['trac.*', 'bitten.*']) cmlenz@410: self.env.path = tempfile.mkdtemp() cmlenz@410: cmlenz@410: # Create tables cmlenz@410: db = self.env.get_db_cnx() cmlenz@410: cursor = db.cursor() cmlenz@410: connector, _ = DatabaseManager(self.env)._get_connector() cmlenz@410: for table in schema: cmlenz@410: for stmt in connector.to_sql(table): cmlenz@410: cursor.execute(stmt) cmlenz@410: cmlenz@410: # Set up permissions cmlenz@410: self.env.config.set('trac', 'permission_store', cmlenz@410: 'DefaultPermissionStore') cmlenz@410: cmlenz@410: # Hook up a dummy repository cmlenz@410: self.repos = Mock( cmlenz@410: get_node=lambda path, rev=None: Mock(get_history=lambda: [], cmlenz@410: isdir=True), cmlenz@410: normalize_path=lambda path: path, osimons@836: normalize_rev=lambda rev: rev, dfraser@562: sync=lambda: None, cmlenz@410: ) dfraser@562: self.repos.authz = Mock(has_permission=lambda path: True, assert_permission=lambda path: None) cmlenz@410: self.env.get_repository = lambda authname=None: self.repos cmlenz@410: cmlenz@410: def tearDown(self): cmlenz@410: shutil.rmtree(self.env.path) cmlenz@410: osimons@575: osimons@575: class BuildConfigControllerTestCase(AbstractWebUITestCase): osimons@575: cmlenz@410: def test_overview(self): cmlenz@410: PermissionSystem(self.env).grant_permission('joe', 'BUILD_VIEW') cmlenz@410: req = Mock(method='GET', base_path='', cgi_location='', cmlenz@410: path_info='/build', href=Href('/trac'), args={}, chrome={}, dfraser@562: perm=PermissionCache(self.env, 'joe'), authname='joe') cmlenz@410: cmlenz@410: module = BuildConfigController(self.env) cmlenz@410: assert module.match_request(req) wbell@503: _, data, _ = module.process_request(req) cmlenz@410: wbell@503: self.assertEqual('overview', data['page_mode']) cmlenz@410: cmlenz@410: def test_view_config(self): cmlenz@410: config = BuildConfig(self.env, name='test', path='trunk') cmlenz@410: config.insert() cmlenz@410: platform = TargetPlatform(self.env, config='test', name='any') cmlenz@410: platform.insert() cmlenz@410: cmlenz@410: PermissionSystem(self.env).grant_permission('joe', 'BUILD_VIEW') cmlenz@410: req = Mock(method='GET', base_path='', cgi_location='', cmlenz@410: path_info='/build/test', href=Href('/trac'), args={}, wbell@503: chrome={}, authname='joe', cmlenz@410: perm=PermissionCache(self.env, 'joe')) cmlenz@410: cmlenz@410: root = Mock(get_entries=lambda: ['foo'], cmlenz@410: get_history=lambda: [('trunk', rev, 'edit') for rev in cmlenz@410: range(123, 111, -1)]) cmlenz@410: self.repos = Mock(get_node=lambda path, rev=None: root, osimons@836: sync=lambda: None, normalize_path=lambda path: path, osimons@884: normalize_rev=lambda rev: rev, youngest_rev=123) dfraser@562: self.repos.authz = Mock(has_permission=lambda path: True, assert_permission=lambda path: None) cmlenz@410: cmlenz@410: module = BuildConfigController(self.env) cmlenz@410: assert module.match_request(req) wbell@503: _, data, _ = module.process_request(req) cmlenz@410: wbell@503: self.assertEqual('view_config', data['page_mode']) wbell@503: assert not 'next' in req.chrome['links'] cmlenz@410: osimons@629: from trac.resource import Resource osimons@629: self.assertEquals(Resource('build', 'test'), data['context'].resource) osimons@629: osimons@629: self.assertEquals([], data['config']['attachments']['attachments']) osimons@629: self.assertEquals('/trac/attachment/build/test/', osimons@629: data['config']['attachments']['attach_href']) osimons@629: osimons@813: def test_bitten_keeps_order_of_revisions_from_versioncontrol(self): osimons@813: # Trac's API specifies that they are sorted chronological (backwards) osimons@813: # We must not assume that these revision numbers can be sorted later on, osimons@813: # for example the mercurial plugin will return the revisions as strings osimons@813: # (e.g. '880:4c19fa95fb9e') osimons@813: config = BuildConfig(self.env, name='test', path='trunk') osimons@813: config.insert() osimons@813: platform = TargetPlatform(self.env, config='test', name='any') osimons@813: platform.insert() osimons@813: osimons@813: PermissionSystem(self.env).grant_permission('joe', 'BUILD_VIEW') osimons@813: req = Mock(method='GET', base_path='', cgi_location='', osimons@813: path_info='/build/'+config.name, href=Href('/trac'), args={}, osimons@813: chrome={}, authname='joe', osimons@813: perm=PermissionCache(self.env, 'joe')) osimons@813: osimons@813: # revisions are intentionally not sorted in any way - bitten should just keep them! osimons@813: revision_ids = [5, 8, 2] osimons@813: revision_list = [('trunk', revision, 'edit') for revision in revision_ids] osimons@813: root = Mock(get_entries=lambda: ['foo'], get_history=lambda: revision_list) osimons@813: self.repos = Mock(get_node=lambda path, rev=None: root, osimons@836: sync=lambda: None, normalize_path=lambda path: path, osimons@884: normalize_rev=lambda rev: rev, youngest_rev=5) osimons@813: self.repos.authz = Mock(has_permission=lambda path: True, assert_permission=lambda path: None) osimons@813: osimons@813: module = BuildConfigController(self.env) osimons@813: assert module.match_request(req) osimons@813: _, data, _ = module.process_request(req) osimons@813: osimons@813: actual_revision_ids = data['config']['revisions'] osimons@813: self.assertEquals(revision_ids, actual_revision_ids) osimons@813: cmlenz@410: def test_view_config_paging(self): cmlenz@410: config = BuildConfig(self.env, name='test', path='trunk') cmlenz@410: config.insert() cmlenz@410: platform = TargetPlatform(self.env, config='test', name='any') cmlenz@410: platform.insert() cmlenz@410: cmlenz@410: PermissionSystem(self.env).grant_permission('joe', 'BUILD_VIEW') cmlenz@410: req = Mock(method='GET', base_path='', cgi_location='', cmlenz@410: path_info='/build/test', href=Href('/trac'), args={}, wbell@503: chrome={}, authname='joe', cmlenz@410: perm=PermissionCache(self.env, 'joe')) cmlenz@410: cmlenz@410: root = Mock(get_entries=lambda: ['foo'], cmlenz@410: get_history=lambda: [('trunk', rev, 'edit') for rev in cmlenz@410: range(123, 110, -1)]) cmlenz@410: self.repos = Mock(get_node=lambda path, rev=None: root, osimons@836: sync=lambda: None, normalize_path=lambda path: path, osimons@884: normalize_rev=lambda rev: rev, youngest_rev=123) dfraser@562: self.repos.authz = Mock(has_permission=lambda path: True, assert_permission=lambda path: None) cmlenz@410: cmlenz@410: module = BuildConfigController(self.env) cmlenz@410: assert module.match_request(req) wbell@503: _, data, _ = module.process_request(req) cmlenz@410: wbell@503: if req.chrome: cmlenz@410: self.assertEqual('/trac/build/test?page=2', cmlenz@410: req.chrome['links']['next'][0]['href']) cmlenz@410: osimons@575: def test_raise_404(self): osimons@575: PermissionSystem(self.env).grant_permission('joe', 'BUILD_VIEW') osimons@575: module = BuildConfigController(self.env) osimons@575: req = Mock(method='GET', base_path='', cgi_location='', osimons@575: path_info='/build/nonexisting', href=Href('/trac'), args={}, osimons@575: chrome={}, authname='joe', osimons@575: perm=PermissionCache(self.env, 'joe')) osimons@575: self.failUnless(module.match_request(req)) osimons@575: try: osimons@575: module.process_request(req) osimons@575: except Exception, e: osimons@575: self.failUnless(isinstance(e, HTTPNotFound)) osimons@575: self.assertEquals(str(e), "404 Not Found (Build configuration " osimons@575: "'nonexisting' does not exist.)") osimons@575: return osimons@575: self.fail("This should have raised HTTPNotFound") cmlenz@439: cmlenz@439: osimons@575: class BuildControllerTestCase(AbstractWebUITestCase): cmlenz@439: osimons@629: def test_view_build(self): osimons@629: config = BuildConfig(self.env, name='test', path='trunk') osimons@629: config.insert() osimons@629: platform = TargetPlatform(self.env, config='test', name='any') osimons@629: platform.insert() osimons@629: build = Build(self.env, config='test', platform=1, rev=123, rev_time=42, osimons@629: status=Build.SUCCESS, slave='hal') osimons@629: build.insert() osimons@629: osimons@629: PermissionSystem(self.env).grant_permission('joe', 'BUILD_VIEW') osimons@629: req = Mock(method='GET', base_path='', cgi_location='', osimons@629: path_info='/build/test/1', href=Href('/trac'), args={}, osimons@629: chrome={}, authname='joe', osimons@629: perm=PermissionCache(self.env, 'joe')) osimons@629: osimons@629: root = Mock(get_entries=lambda: ['foo'], osimons@629: get_history=lambda: [('trunk', rev, 'edit') for rev in osimons@629: range(123, 111, -1)]) osimons@629: self.repos = Mock(get_node=lambda path, rev=None: root, osimons@629: sync=lambda: None, osimons@629: normalize_path=lambda path: path, osimons@836: normalize_rev=lambda rev: rev, osimons@629: get_changeset=lambda rev: Mock(author='joe')) osimons@629: self.repos.authz = Mock(has_permission=lambda path: True, assert_permission=lambda path: None) osimons@629: osimons@629: module = BuildController(self.env) osimons@629: assert module.match_request(req) osimons@629: _, data, _ = module.process_request(req) osimons@629: osimons@629: self.assertEqual('view_build', data['page_mode']) osimons@629: osimons@629: from trac.resource import Resource osimons@629: self.assertEquals(Resource('build', 'test/1'), data['context'].resource) osimons@629: osimons@629: self.assertEquals([], data['build']['attachments']['attachments']) osimons@629: self.assertEquals('/trac/attachment/build/test/1/', osimons@629: data['build']['attachments']['attach_href']) osimons@629: osimons@575: def test_raise_404(self): osimons@575: PermissionSystem(self.env).grant_permission('joe', 'BUILD_VIEW') osimons@575: module = BuildController(self.env) osimons@575: config = BuildConfig(self.env, name='existing', path='trunk') osimons@575: config.insert() osimons@575: req = Mock(method='GET', base_path='', cgi_location='', osimons@575: path_info='/build/existing/42', href=Href('/trac'), args={}, osimons@575: chrome={}, authname='joe', osimons@575: perm=PermissionCache(self.env, 'joe')) osimons@575: self.failUnless(module.match_request(req)) osimons@575: try: osimons@575: module.process_request(req) osimons@575: except Exception, e: osimons@575: self.failUnless(isinstance(e, HTTPNotFound)) osimons@575: self.assertEquals(str(e), osimons@575: "404 Not Found (Build '42' does not exist.)") osimons@575: return osimons@575: self.fail("This should have raised HTTPNotFound") osimons@575: osimons@629: osimons@575: class SourceFileLinkFormatterTestCase(AbstractWebUITestCase): cmlenz@439: cmlenz@439: def test_format_simple_link_in_repos(self): cmlenz@439: BuildConfig(self.env, name='test', path='trunk').insert() cmlenz@439: build = Build(self.env, config='test', platform=1, rev=123, rev_time=42, cmlenz@439: status=Build.SUCCESS, slave='hal') cmlenz@439: build.insert() cmlenz@439: step = BuildStep(self.env, build=build.id, name='foo', cmlenz@439: status=BuildStep.SUCCESS) cmlenz@439: step.insert() cmlenz@439: cmlenz@439: self.repos.get_node = lambda path, rev: (path, rev) cmlenz@439: cmlenz@439: req = Mock(method='GET', href=Href('/trac'), authname='hal') cmlenz@439: comp = SourceFileLinkFormatter(self.env) cmlenz@439: formatter = comp.get_formatter(req, build) cmlenz@439: osimons@593: # posix cmlenz@440: output = formatter(step, None, None, u'error in foo/bar.c: bad') cmlenz@439: self.assertEqual(Markup, type(output)) cmlenz@439: self.assertEqual('error in ' cmlenz@440: 'foo/bar.c: bad', output) osimons@593: # windows osimons@593: output = formatter(step, None, None, u'error in foo\\win.c: bad') osimons@593: self.assertEqual(Markup, type(output)) osimons@593: self.assertEqual(r'error in ' osimons@593: 'foo\win.c: bad', output) cmlenz@439: dfraser@528: def test_format_bad_links(self): dfraser@528: BuildConfig(self.env, name='test', path='trunk').insert() dfraser@528: build = Build(self.env, config='test', platform=1, rev=123, rev_time=42, dfraser@528: status=Build.SUCCESS, slave='hal') dfraser@528: build.insert() dfraser@528: step = BuildStep(self.env, build=build.id, name='foo', dfraser@528: status=BuildStep.SUCCESS) dfraser@528: step.insert() dfraser@528: dfraser@528: self.repos.get_node = lambda path, rev: (path, rev) dfraser@528: dfraser@528: req = Mock(method='GET', href=Href('/trac'), authname='hal') dfraser@528: comp = SourceFileLinkFormatter(self.env) dfraser@528: formatter = comp.get_formatter(req, build) dfraser@528: dfraser@528: output = formatter(step, None, None, u'Linking -I../.. with ../libtool') dfraser@528: self.assertEqual(Markup, type(output)) dfraser@528: self.assertEqual('Linking -I../.. with ../libtool', output) dfraser@528: cmlenz@439: def test_format_simple_link_not_in_repos(self): cmlenz@439: BuildConfig(self.env, name='test', path='trunk').insert() cmlenz@439: build = Build(self.env, config='test', platform=1, rev=123, rev_time=42, cmlenz@439: status=Build.SUCCESS, slave='hal') cmlenz@439: build.insert() cmlenz@439: step = BuildStep(self.env, build=build.id, name='foo', cmlenz@439: status=BuildStep.SUCCESS) cmlenz@439: step.insert() cmlenz@439: cmlenz@439: def _raise(): cmlenz@439: raise TracError('No such node') cmlenz@439: self.repos.get_node = lambda path, rev: _raise() cmlenz@439: cmlenz@439: req = Mock(method='GET', href=Href('/trac'), authname='hal') cmlenz@439: comp = SourceFileLinkFormatter(self.env) cmlenz@439: formatter = comp.get_formatter(req, build) cmlenz@439: cmlenz@440: output = formatter(step, None, None, u'error in foo/bar.c: bad') cmlenz@439: self.assertEqual(Markup, type(output)) cmlenz@440: self.assertEqual('error in foo/bar.c: bad', output) cmlenz@439: cmlenz@439: def test_format_link_in_repos_with_line(self): cmlenz@439: BuildConfig(self.env, name='test', path='trunk').insert() cmlenz@439: build = Build(self.env, config='test', platform=1, rev=123, rev_time=42, cmlenz@439: status=Build.SUCCESS, slave='hal') cmlenz@439: build.insert() cmlenz@439: step = BuildStep(self.env, build=build.id, name='foo', cmlenz@439: status=BuildStep.SUCCESS) cmlenz@439: step.insert() cmlenz@439: cmlenz@439: self.repos.get_node = lambda path, rev: (path, rev) cmlenz@439: cmlenz@439: req = Mock(method='GET', href=Href('/trac'), authname='hal') cmlenz@439: comp = SourceFileLinkFormatter(self.env) cmlenz@439: formatter = comp.get_formatter(req, build) cmlenz@439: osimons@593: # posix cmlenz@440: output = formatter(step, None, None, u'error in foo/bar.c:123: bad') cmlenz@439: self.assertEqual(Markup, type(output)) cmlenz@439: self.assertEqual('error in ' cmlenz@440: 'foo/bar.c:123: bad', output) osimons@593: # windows osimons@593: output = formatter(step, None, None, u'error in foo\\win.c:123: bad') osimons@593: self.assertEqual(Markup, type(output)) osimons@593: self.assertEqual(r'error in ' osimons@593: 'foo\win.c:123: bad', output) cmlenz@439: cmlenz@439: def test_format_link_not_in_repos_with_line(self): cmlenz@439: BuildConfig(self.env, name='test', path='trunk').insert() cmlenz@439: build = Build(self.env, config='test', platform=1, rev=123, rev_time=42, cmlenz@439: status=Build.SUCCESS, slave='hal') cmlenz@439: build.insert() cmlenz@439: step = BuildStep(self.env, build=build.id, name='foo', cmlenz@439: status=BuildStep.SUCCESS) cmlenz@439: step.insert() cmlenz@439: cmlenz@439: def _raise(): cmlenz@439: raise TracError('No such node') cmlenz@439: self.repos.get_node = lambda path, rev: _raise() cmlenz@439: cmlenz@439: req = Mock(method='GET', href=Href('/trac'), authname='hal') cmlenz@439: comp = SourceFileLinkFormatter(self.env) cmlenz@439: formatter = comp.get_formatter(req, build) cmlenz@439: cmlenz@440: output = formatter(step, None, None, u'error in foo/bar.c:123: bad') cmlenz@439: self.assertEqual(Markup, type(output)) cmlenz@440: self.assertEqual('error in foo/bar.c:123: bad', output) cmlenz@439: cmlenz@439: cmlenz@410: def suite(): cmlenz@439: suite = unittest.TestSuite() cmlenz@439: suite.addTest(unittest.makeSuite(BuildConfigControllerTestCase, 'test')) osimons@575: suite.addTest(unittest.makeSuite(BuildControllerTestCase, 'test')) cmlenz@439: suite.addTest(unittest.makeSuite(SourceFileLinkFormatterTestCase, 'test')) cmlenz@439: return suite cmlenz@410: cmlenz@410: if __name__ == '__main__': cmlenz@410: unittest.main(defaultTest='suite')