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
18
from __future__ import absolute_import
20
from html import escape
24
from io import BytesIO
26
from breezy.tests import TestCaseWithTransport
10
27
from configobj import ConfigObj
28
from breezy import config
12
from loggerhead.history import History
13
from loggerhead.apps.branch import BranchWSGIApp
30
from ..apps.branch import BranchWSGIApp
31
from ..apps.http_head import HeadMiddleware
14
32
from paste.fixture import TestApp
17
def test_config_root():
18
from loggerhead.apps.config import Root
20
app = TestApp(Root(config))
22
res.mustcontain('loggerhead branches')
25
class BasicTests(object):
27
# setup_method and teardown_method are so i can run the tests with
28
# py.test and take advantage of the error reporting.
29
def setup_method(self, meth):
32
def teardown_method(self, meth):
33
from paste.httpexceptions import HTTPExceptionHandler, HTTPMovedPermanently
35
from .fixtures import (
40
class BasicTests(TestCaseWithTransport):
36
logging.basicConfig(level=logging.DEBUG)
38
self.old_bzrhome = None
43
TestCaseWithTransport.setUp(self)
44
logging.basicConfig(level=logging.ERROR)
45
logging.getLogger('bzr').setLevel(logging.CRITICAL)
40
47
def createBranch(self):
41
self.old_bzrhome = bzrlib.osutils.set_or_unset_env('BZR_HOME', '')
42
self.bzrbranch = tempfile.mkdtemp()
43
self.branch = bzrlib.bzrdir.BzrDir.create_branch_convenience(
44
self.bzrbranch, force_new_tree=True)
45
self.tree = self.branch.bzrdir.open_workingtree()
50
branch_name = 'branch'
54
def setUpLoggerhead(self):
55
app = TestApp(BranchWSGIApp(self.branch).app)
59
if self.bzrbranch is not None:
60
shutil.rmtree(self.bzrbranch)
61
bzrlib.osutils.set_or_unset_env('BZR_HOME', self.old_bzrhome)
48
self.tree = self.make_branch_and_tree('.')
50
def setUpLoggerhead(self, **kw):
51
branch_app = BranchWSGIApp(self.tree.branch, '', **kw).app
52
return TestApp(HTTPExceptionHandler(branch_app))
54
def assertOkJsonResponse(self, app, env):
55
start, content = consume_app(app, env)
56
self.assertEqual('200 OK', start[0])
57
self.assertEqual('application/json', dict(start[1])['Content-Type'])
58
self.assertEqual(None, start[2])
59
json.loads(content.decode('UTF-8'))
61
def make_branch_app(self, branch, **kw):
62
branch_app = BranchWSGIApp(branch, friendly_name='friendly-name', **kw)
63
branch_app._environ = {
68
branch_app._url_base = ''
64
72
class TestWithSimpleTree(BasicTests):
67
75
BasicTests.setUp(self)
70
f = open(os.path.join(self.bzrbranch, 'myfilename'), 'w')
71
self.filecontents = ('some\nmultiline\ndata\n'
72
'with<htmlspecialchars\n')
74
f.write(self.filecontents)
77
self.tree.add('myfilename')
78
self.fileid = self.tree.path2id('myfilename')
79
self.msg = 'a very exciting commit message <'
80
self.revid = self.tree.commit(message=self.msg)
76
self.sample_branch_fixture = SampleBranch(self)
78
# XXX: This could be cleaned up more... -- mbp 2011-11-25
79
self.useFixture(self.sample_branch_fixture)
80
self.tree = self.sample_branch_fixture.tree
81
self.path = self.sample_branch_fixture.path
82
self.filecontents = self.sample_branch_fixture.filecontents
83
self.msg = self.sample_branch_fixture.msg
85
def test_public_private(self):
86
app = self.make_branch_app(self.tree.branch, private=True)
87
self.assertEqual(app.public_private_css(), 'private')
88
app = self.make_branch_app(self.tree.branch)
89
self.assertEqual(app.public_private_css(), 'public')
83
91
def test_changes(self):
84
92
app = self.setUpLoggerhead()
85
93
res = app.get('/changes')
86
res.mustcontain(cgi.escape(self.msg))
94
res.mustcontain(escape(self.msg))
96
def test_changes_for_file(self):
97
app = self.setUpLoggerhead()
98
res = app.get('/changes?filter_path=%s' % self.path)
99
res.mustcontain(escape(self.msg))
101
def test_changes_branch_from(self):
102
app = self.setUpLoggerhead(served_url="lp:loggerhead")
103
res = app.get('/changes')
104
self.failUnless("To get this branch, use:" in res)
105
self.failUnless("lp:loggerhead" in res)
88
107
def test_changes_search(self):
89
108
app = self.setUpLoggerhead()
93
112
def test_annotate(self):
94
113
app = self.setUpLoggerhead()
95
res = app.get('/annotate', params={'file_id':self.fileid})
114
res = app.get('/annotate/1/%s' % self.path, params={})
115
# If pygments is installed, it inserts <span class="pyg" content into
116
# the output, to trigger highlighting. And it specifically highlights
117
# the < that we are interested in seeing in the output.
118
# Without pygments we have a simple: 'with<htmlspecialchars'
120
# '<span class='pyg-n'>with</span><span class='pyg-o'><</span>'
121
# '<span class='pyg-n'>htmlspecialchars</span>
122
# So we pre-filter the body, to make sure remove spans of that type.
123
body_no_span = re.sub(b'<span class="pyg-.">', b'', res.body)
124
body_no_span = body_no_span.replace(b'</span>', b'')
96
125
for line in self.filecontents.splitlines():
97
res.mustcontain(cgi.escape(line))
126
escaped = escape(line).encode('utf-8')
127
self.assertTrue(escaped in body_no_span,
128
"did not find %r in %r" % (escaped, body_no_span))
99
130
def test_inventory(self):
100
131
app = self.setUpLoggerhead()
101
132
res = app.get('/files')
102
133
res.mustcontain('myfilename')
134
res = app.get('/files/')
135
res.mustcontain('myfilename')
136
res = app.get('/files/1')
137
res.mustcontain('myfilename')
138
res = app.get('/files/1/')
139
res.mustcontain('myfilename')
141
def test_inventory_bad_rev_404(self):
142
app = self.setUpLoggerhead()
143
res = app.get('/files/200', status=404)
144
res = app.get('/files/invalid-revid', status=404)
146
def test_inventory_bad_path_404(self):
147
app = self.setUpLoggerhead()
148
res = app.get('/files/1/hooha', status=404)
104
150
def test_revision(self):
105
151
app = self.setUpLoggerhead()
106
152
res = app.get('/revision/1')
153
res.mustcontain(no=['anotherfile<'])
154
res.mustcontain('anotherfile<')
107
155
res.mustcontain('myfilename')
110
158
class TestEmptyBranch(BasicTests):
159
"""Test that an empty branch doesn't break"""
113
162
BasicTests.setUp(self)
118
167
res = app.get('/changes')
119
168
res.mustcontain('No revisions!')
170
def test_inventory(self):
171
app = self.setUpLoggerhead()
172
res = app.get('/files')
173
res.mustcontain('No revisions!')
176
class TestHiddenBranch(BasicTests):
178
Test that hidden branches aren't shown
179
FIXME: not tested that it doesn't show up on listings
183
BasicTests.setUp(self)
186
locations = config.locations_config_filename()
187
except AttributeError:
188
from breezy import bedding
189
locations = bedding.locations_config_path()
190
ensure_config_dir_exists = bedding.ensure_config_dir_exists
192
ensure_config_dir_exists = config.ensure_config_dir_exists
193
ensure_config_dir_exists()
194
with open(locations, 'w') as f:
195
f.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(b'', 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)