1
# Copyright (C) 2007, 2008, 2009, 2011 Canonical Ltd.
3
# This program is free software; you can redistribute it and/or modify
4
# it under the terms of the GNU General Public License as published by
5
# the Free Software Foundation; either version 2 of the License, or
6
# (at your option) any later version.
8
# This program is distributed in the hope that it will be useful,
9
# but WITHOUT ANY WARRANTY; without even the implied warranty of
10
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11
# GNU General Public License for more details.
13
# You should have received a copy of the GNU General Public License
14
# along with this program; if not, write to the Free Software
15
# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
22
from cStringIO import StringIO
24
from bzrlib.tests import TestCaseWithTransport
26
from bzrlib.util.configobj.configobj import ConfigObj
28
from configobj import ConfigObj
29
from bzrlib import config
9
from configobj import ConfigObj
31
11
from loggerhead.apps.branch import BranchWSGIApp
32
from loggerhead.apps.http_head import HeadMiddleware
33
12
from paste.fixture import TestApp
34
from paste.httpexceptions import HTTPExceptionHandler, HTTPMovedPermanently
38
class BasicTests(TestCaseWithTransport):
15
def test_config_root():
16
from loggerhead.apps.config import Root
18
app = TestApp(Root(config))
20
res.mustcontain('loggerhead branches')
23
class BasicTests(object):
25
# setup_method and teardown_method are so i can run the tests with
26
# py.test and take advantage of the error reporting.
27
def setup_method(self, meth):
30
def teardown_method(self, meth):
41
TestCaseWithTransport.setUp(self)
42
logging.basicConfig(level=logging.ERROR)
43
logging.getLogger('bzr').setLevel(logging.CRITICAL)
34
logging.basicConfig(level=logging.DEBUG)
36
self.old_bzrhome = None
45
38
def createBranch(self):
46
self.tree = self.make_branch_and_tree('.')
48
def setUpLoggerhead(self, **kw):
49
branch_app = BranchWSGIApp(self.tree.branch, '', export_tarballs=True, **kw).app
50
return TestApp(HTTPExceptionHandler(branch_app))
52
def assertOkJsonResponse(self, app, env):
53
start, content = consume_app(app, env)
54
self.assertEqual('200 OK', start[0])
55
self.assertEqual('application/json', dict(start[1])['Content-Type'])
56
self.assertEqual(None, start[2])
57
simplejson.loads(content)
59
def make_branch_app(self, branch, **kw):
60
branch_app = BranchWSGIApp(branch, friendly_name='friendly-name', **kw)
61
branch_app._environ = {
66
branch_app._url_base = ''
39
self.old_bzrhome = bzrlib.osutils.set_or_unset_env('BZR_HOME', '')
40
self.bzrbranch = tempfile.mkdtemp()
41
self.branch = bzrlib.bzrdir.BzrDir.create_branch_convenience(
42
self.bzrbranch, force_new_tree=True)
43
self.tree = self.branch.bzrdir.open_workingtree()
48
branch_name = 'branch'
52
def setUpLoggerhead(self):
53
app = TestApp(BranchWSGIApp(self.branch, '').app)
57
if self.bzrbranch is not None:
58
shutil.rmtree(self.bzrbranch)
59
bzrlib.osutils.set_or_unset_env('BZR_HOME', self.old_bzrhome)
70
62
class TestWithSimpleTree(BasicTests):
73
65
BasicTests.setUp(self)
74
66
self.createBranch()
68
f = open(os.path.join(self.bzrbranch, 'myfilename'), 'w')
76
69
self.filecontents = ('some\nmultiline\ndata\n'
77
70
'with<htmlspecialchars\n')
78
filenames = ['myfilename', 'anotherfile<']
79
self.build_tree_contents(
80
(filename, self.filecontents) for filename in filenames)
81
for filename in filenames:
82
self.tree.add(filename, '%s-id' % filename)
72
f.write(self.filecontents)
75
self.tree.add('myfilename')
83
76
self.fileid = self.tree.path2id('myfilename')
84
77
self.msg = 'a very exciting commit message <'
85
78
self.revid = self.tree.commit(message=self.msg)
87
def test_public_private(self):
88
app = self.make_branch_app(self.tree.branch, private=True)
89
self.assertEqual(app.public_private_css(), 'private')
90
app = self.make_branch_app(self.tree.branch)
91
self.assertEqual(app.public_private_css(), 'public')
93
81
def test_changes(self):
94
82
app = self.setUpLoggerhead()
95
83
res = app.get('/changes')
96
84
res.mustcontain(cgi.escape(self.msg))
98
def test_changes_for_file(self):
99
app = self.setUpLoggerhead()
100
res = app.get('/changes?filter_file_id=myfilename-id')
101
res.mustcontain(cgi.escape(self.msg))
103
def test_changes_branch_from(self):
104
app = self.setUpLoggerhead(served_url="lp:loggerhead")
105
res = app.get('/changes')
106
self.failUnless("To get this branch, use:" in res)
107
self.failUnless("lp:loggerhead" in res)
108
app = self.setUpLoggerhead(served_url=None)
109
res = app.get('/changes')
110
self.failIf("To get this branch, use:" in res)
112
86
def test_changes_search(self):
113
87
app = self.setUpLoggerhead()
114
88
res = app.get('/changes', params={'q': 'foo'})
117
91
def test_annotate(self):
118
92
app = self.setUpLoggerhead()
119
res = app.get('/annotate', params={'file_id': self.fileid})
120
# If pygments is installed, it inserts <span class="pyg" content into
121
# the output, to trigger highlighting. And it specifically highlights
122
# the < that we are interested in seeing in the output.
123
# Without pygments we have a simple: 'with<htmlspecialchars'
125
# '<span class='pyg-n'>with</span><span class='pyg-o'><</span>'
126
# '<span class='pyg-n'>htmlspecialchars</span>
127
# So we pre-filter the body, to make sure remove spans of that type.
128
body_no_span = re.sub(r'<span class="pyg-.">', '', res.body)
129
body_no_span = body_no_span.replace('</span>', '')
93
res = app.get('/annotate', params={'file_id':self.fileid})
130
94
for line in self.filecontents.splitlines():
131
escaped = cgi.escape(line)
132
self.assertTrue(escaped in body_no_span,
133
"did not find %r in %r" % (escaped, body_no_span))
95
res.mustcontain(cgi.escape(line))
135
97
def test_inventory(self):
136
98
app = self.setUpLoggerhead()
137
99
res = app.get('/files')
138
100
res.mustcontain('myfilename')
139
res = app.get('/files/')
140
res.mustcontain('myfilename')
141
res = app.get('/files/1')
142
res.mustcontain('myfilename')
143
res = app.get('/files/1/')
144
res.mustcontain('myfilename')
145
res = app.get('/files/1/?file_id=' + self.tree.path2id(''))
146
res.mustcontain('myfilename')
148
def test_inventory_bad_rev_404(self):
149
app = self.setUpLoggerhead()
150
res = app.get('/files/200', status=404)
151
res = app.get('/files/invalid-revid', status=404)
153
def test_inventory_bad_path_404(self):
154
app = self.setUpLoggerhead()
155
res = app.get('/files/1/hooha', status=404)
156
res = app.get('/files/1?file_id=dssadsada', status=404)
158
102
def test_revision(self):
159
103
app = self.setUpLoggerhead()
160
104
res = app.get('/revision/1')
161
res.mustcontain(no=['anotherfile<'])
162
res.mustcontain('anotherfile<')
163
105
res.mustcontain('myfilename')
166
108
class TestEmptyBranch(BasicTests):
167
"""Test that an empty branch doesn't break"""
170
111
BasicTests.setUp(self)
175
116
res = app.get('/changes')
176
117
res.mustcontain('No revisions!')
178
def test_inventory(self):
179
app = self.setUpLoggerhead()
180
res = app.get('/files')
181
res.mustcontain('No revisions!')
184
class TestHiddenBranch(BasicTests):
186
Test that hidden branches aren't shown
187
FIXME: not tested that it doesn't show up on listings
191
BasicTests.setUp(self)
193
locations = config.locations_config_filename()
194
config.ensure_config_dir_exists()
195
open(locations, 'wb').write('[%s]\nhttp_serve = False'
196
% (self.tree.branch.base,))
198
def test_no_access(self):
199
app = self.setUpLoggerhead()
200
res = app.get('/changes', status=404)
203
class TestControllerRedirects(BasicTests):
205
Test that a file under /files redirects to /view,
206
and a directory under /view redirects to /files.
210
BasicTests.setUp(self)
212
self.build_tree(('file', 'folder/', 'folder/file'))
213
self.tree.smart_add([])
216
def test_view_folder(self):
217
app = TestApp(BranchWSGIApp(self.tree.branch, '').app)
219
e = self.assertRaises(HTTPMovedPermanently, app.get, '/view/head:/folder')
220
self.assertEqual(e.location(), '/files/head:/folder')
222
def test_files_file(self):
223
app = TestApp(BranchWSGIApp(self.tree.branch, '').app)
225
e = self.assertRaises(HTTPMovedPermanently, app.get, '/files/head:/folder/file')
226
self.assertEqual(e.location(), '/view/head:/folder/file')
227
e = self.assertRaises(HTTPMovedPermanently, app.get, '/files/head:/file')
228
self.assertEqual(e.location(), '/view/head:/file')
231
class TestHeadMiddleware(BasicTests):
234
BasicTests.setUp(self)
236
self.msg = 'trivial commit message'
237
self.revid = self.tree.commit(message=self.msg)
239
def setUpLoggerhead(self, **kw):
240
branch_app = BranchWSGIApp(self.tree.branch, '', **kw).app
241
return TestApp(HTTPExceptionHandler(HeadMiddleware(branch_app)))
244
app = self.setUpLoggerhead()
245
res = app.get('/changes')
246
res.mustcontain(self.msg)
247
self.assertEqual('text/html', res.header('Content-Type'))
250
app = self.setUpLoggerhead()
251
res = app.get('/changes', extra_environ={'REQUEST_METHOD': 'HEAD'})
252
self.assertEqual('text/html', res.header('Content-Type'))
253
self.assertEqualDiff('', res.body)
256
def consume_app(app, env):
259
def start_response(status, headers, exc_info=None):
260
start.append((status, headers, exc_info))
262
extra_content = list(app(env, start_response))
263
body.writelines(extra_content)
264
return start[0], body.getvalue()
268
#class TestGlobalConfig(BasicTests):
270
# Test that global config settings are respected
274
# BasicTests.setUp(self)
275
# self.createBranch()
276
# config.GlobalConfig().set_user_option('http_version', 'True')
278
# def test_setting_respected(self):
279
#FIXME: Figure out how to test this properly
280
# app = self.setUpLoggerhead()
281
# res = app.get('/changes', status=200)