##// END OF EJS Templates
hgwebdir: split out makeindex function, facilitate test failure diagnosis
Dirkjan Ochtman -
r5601:8279cb84 default
parent child Browse files
Show More
@@ -1,262 +1,261 b''
1 # hgweb/hgwebdir_mod.py - Web interface for a directory of repositories.
1 # hgweb/hgwebdir_mod.py - Web interface for a directory of repositories.
2 #
2 #
3 # Copyright 21 May 2005 - (c) 2005 Jake Edge <jake@edge2.net>
3 # Copyright 21 May 2005 - (c) 2005 Jake Edge <jake@edge2.net>
4 # Copyright 2005, 2006 Matt Mackall <mpm@selenic.com>
4 # Copyright 2005, 2006 Matt Mackall <mpm@selenic.com>
5 #
5 #
6 # This software may be used and distributed according to the terms
6 # This software may be used and distributed according to the terms
7 # of the GNU General Public License, incorporated herein by reference.
7 # of the GNU General Public License, incorporated herein by reference.
8
8
9 import os, mimetools, cStringIO
9 import os, mimetools, cStringIO
10 from mercurial.i18n import gettext as _
10 from mercurial.i18n import gettext as _
11 from mercurial import ui, hg, util, templater
11 from mercurial import ui, hg, util, templater
12 from common import ErrorResponse, get_mtime, staticfile, style_map, paritygen
12 from common import ErrorResponse, get_mtime, staticfile, style_map, paritygen
13 from hgweb_mod import hgweb
13 from hgweb_mod import hgweb
14 from request import wsgirequest
14 from request import wsgirequest
15
15
16 # This is a stopgap
16 # This is a stopgap
17 class hgwebdir(object):
17 class hgwebdir(object):
18 def __init__(self, config, parentui=None):
18 def __init__(self, config, parentui=None):
19 def cleannames(items):
19 def cleannames(items):
20 return [(util.pconvert(name).strip('/'), path)
20 return [(util.pconvert(name).strip('/'), path)
21 for name, path in items]
21 for name, path in items]
22
22
23 self.parentui = parentui
23 self.parentui = parentui or ui.ui(report_untrusted=False,
24 interactive = False)
24 self.motd = None
25 self.motd = None
25 self.style = None
26 self.style = None
26 self.stripecount = None
27 self.stripecount = None
27 self.repos_sorted = ('name', False)
28 self.repos_sorted = ('name', False)
28 if isinstance(config, (list, tuple)):
29 if isinstance(config, (list, tuple)):
29 self.repos = cleannames(config)
30 self.repos = cleannames(config)
30 self.repos_sorted = ('', False)
31 self.repos_sorted = ('', False)
31 elif isinstance(config, dict):
32 elif isinstance(config, dict):
32 self.repos = cleannames(config.items())
33 self.repos = cleannames(config.items())
33 self.repos.sort()
34 self.repos.sort()
34 else:
35 else:
35 if isinstance(config, util.configparser):
36 if isinstance(config, util.configparser):
36 cp = config
37 cp = config
37 else:
38 else:
38 cp = util.configparser()
39 cp = util.configparser()
39 cp.read(config)
40 cp.read(config)
40 self.repos = []
41 self.repos = []
41 if cp.has_section('web'):
42 if cp.has_section('web'):
42 if cp.has_option('web', 'motd'):
43 if cp.has_option('web', 'motd'):
43 self.motd = cp.get('web', 'motd')
44 self.motd = cp.get('web', 'motd')
44 if cp.has_option('web', 'style'):
45 if cp.has_option('web', 'style'):
45 self.style = cp.get('web', 'style')
46 self.style = cp.get('web', 'style')
46 if cp.has_option('web', 'stripes'):
47 if cp.has_option('web', 'stripes'):
47 self.stripecount = int(cp.get('web', 'stripes'))
48 self.stripecount = int(cp.get('web', 'stripes'))
48 if cp.has_section('paths'):
49 if cp.has_section('paths'):
49 self.repos.extend(cleannames(cp.items('paths')))
50 self.repos.extend(cleannames(cp.items('paths')))
50 if cp.has_section('collections'):
51 if cp.has_section('collections'):
51 for prefix, root in cp.items('collections'):
52 for prefix, root in cp.items('collections'):
52 for path in util.walkrepos(root):
53 for path in util.walkrepos(root):
53 repo = os.path.normpath(path)
54 repo = os.path.normpath(path)
54 name = repo
55 name = repo
55 if name.startswith(prefix):
56 if name.startswith(prefix):
56 name = name[len(prefix):]
57 name = name[len(prefix):]
57 self.repos.append((name.lstrip(os.sep), repo))
58 self.repos.append((name.lstrip(os.sep), repo))
58 self.repos.sort()
59 self.repos.sort()
59
60
60 def run(self):
61 def run(self):
61 if not os.environ.get('GATEWAY_INTERFACE', '').startswith("CGI/1."):
62 if not os.environ.get('GATEWAY_INTERFACE', '').startswith("CGI/1."):
62 raise RuntimeError("This function is only intended to be called while running as a CGI script.")
63 raise RuntimeError("This function is only intended to be called while running as a CGI script.")
63 import mercurial.hgweb.wsgicgi as wsgicgi
64 import mercurial.hgweb.wsgicgi as wsgicgi
64 wsgicgi.launch(self)
65 wsgicgi.launch(self)
65
66
66 def __call__(self, env, respond):
67 def __call__(self, env, respond):
67 req = wsgirequest(env, respond)
68 req = wsgirequest(env, respond)
68 self.run_wsgi(req)
69 self.run_wsgi(req)
69 return req
70 return req
70
71
71 def run_wsgi(self, req):
72 def run_wsgi(self, req):
72 def header(**map):
73 def header(**map):
73 header_file = cStringIO.StringIO(
74 header_file = cStringIO.StringIO(
74 ''.join(tmpl("header", encoding=util._encoding, **map)))
75 ''.join(tmpl("header", encoding=util._encoding, **map)))
75 msg = mimetools.Message(header_file, 0)
76 msg = mimetools.Message(header_file, 0)
76 req.header(msg.items())
77 req.header(msg.items())
77 yield header_file.read()
78 yield header_file.read()
78
79
79 def footer(**map):
80 def footer(**map):
80 yield tmpl("footer", **map)
81 yield tmpl("footer", **map)
81
82
82 def motd(**map):
83 def motd(**map):
83 if self.motd is not None:
84 if self.motd is not None:
84 yield self.motd
85 yield self.motd
85 else:
86 else:
86 yield config('web', 'motd', '')
87 yield config('web', 'motd', '')
87
88
88 parentui = self.parentui or ui.ui(report_untrusted=False,
89 interactive=False)
90
91 def config(section, name, default=None, untrusted=True):
89 def config(section, name, default=None, untrusted=True):
92 return parentui.config(section, name, default, untrusted)
90 return self.parentui.config(section, name, default, untrusted)
93
91
94 url = req.env.get('SCRIPT_NAME', '')
92 url = req.env.get('SCRIPT_NAME', '')
95 if not url.endswith('/'):
93 if not url.endswith('/'):
96 url += '/'
94 url += '/'
97
95
98 staticurl = config('web', 'staticurl') or url + 'static/'
96 staticurl = config('web', 'staticurl') or url + 'static/'
99 if not staticurl.endswith('/'):
97 if not staticurl.endswith('/'):
100 staticurl += '/'
98 staticurl += '/'
101
99
102 style = self.style
100 style = self.style
103 if style is None:
101 if style is None:
104 style = config('web', 'style', '')
102 style = config('web', 'style', '')
105 if req.form.has_key('style'):
103 if req.form.has_key('style'):
106 style = req.form['style'][0]
104 style = req.form['style'][0]
107 if self.stripecount is None:
105 if self.stripecount is None:
108 self.stripecount = int(config('web', 'stripes', 1))
106 self.stripecount = int(config('web', 'stripes', 1))
109 mapfile = style_map(templater.templatepath(), style)
107 mapfile = style_map(templater.templatepath(), style)
110 tmpl = templater.templater(mapfile, templater.common_filters,
108 tmpl = templater.templater(mapfile, templater.common_filters,
111 defaults={"header": header,
109 defaults={"header": header,
112 "footer": footer,
110 "footer": footer,
113 "motd": motd,
111 "motd": motd,
114 "url": url,
112 "url": url,
115 "staticurl": staticurl})
113 "staticurl": staticurl})
116
114
115 try:
116 try:
117 virtual = req.env.get("PATH_INFO", "").strip('/')
118 if virtual.startswith('static/'):
119 static = os.path.join(templater.templatepath(), 'static')
120 fname = virtual[7:]
121 req.write(staticfile(static, fname, req))
122 elif virtual:
123 repos = dict(self.repos)
124 while virtual:
125 real = repos.get(virtual)
126 if real:
127 req.env['REPO_NAME'] = virtual
128 try:
129 repo = hg.repository(self.parentui, real)
130 hgweb(repo).run_wsgi(req)
131 return
132 except IOError, inst:
133 raise ErrorResponse(500, inst.strerror)
134 except hg.RepoError, inst:
135 raise ErrorResponse(500, str(inst))
136
137 # browse subdirectories
138 subdir = virtual + '/'
139 if [r for r in repos if r.startswith(subdir)]:
140 self.makeindex(req, tmpl, subdir)
141 return
142
143 up = virtual.rfind('/')
144 if up < 0:
145 break
146 virtual = virtual[:up]
147
148 req.respond(404, tmpl("notfound", repo=virtual))
149 else:
150 if req.form.has_key('static'):
151 static = os.path.join(templater.templatepath(), "static")
152 fname = req.form['static'][0]
153 req.write(staticfile(static, fname, req))
154 else:
155 self.makeindex(req, tmpl)
156 except ErrorResponse, err:
157 req.respond(err.code, tmpl('error', error=err.message or ''))
158 finally:
159 tmpl = None
160
161 def makeindex(self, req, tmpl, subdir=""):
162
117 def archivelist(ui, nodeid, url):
163 def archivelist(ui, nodeid, url):
118 allowed = ui.configlist("web", "allow_archive", untrusted=True)
164 allowed = ui.configlist("web", "allow_archive", untrusted=True)
119 for i in [('zip', '.zip'), ('gz', '.tar.gz'), ('bz2', '.tar.bz2')]:
165 for i in [('zip', '.zip'), ('gz', '.tar.gz'), ('bz2', '.tar.bz2')]:
120 if i[0] in allowed or ui.configbool("web", "allow" + i[0],
166 if i[0] in allowed or ui.configbool("web", "allow" + i[0],
121 untrusted=True):
167 untrusted=True):
122 yield {"type" : i[0], "extension": i[1],
168 yield {"type" : i[0], "extension": i[1],
123 "node": nodeid, "url": url}
169 "node": nodeid, "url": url}
124
170
125 def entries(sortcolumn="", descending=False, subdir="", **map):
171 def entries(sortcolumn="", descending=False, subdir="", **map):
126 def sessionvars(**map):
172 def sessionvars(**map):
127 fields = []
173 fields = []
128 if req.form.has_key('style'):
174 if req.form.has_key('style'):
129 style = req.form['style'][0]
175 style = req.form['style'][0]
130 if style != get('web', 'style', ''):
176 if style != get('web', 'style', ''):
131 fields.append(('style', style))
177 fields.append(('style', style))
132
178
133 separator = url[-1] == '?' and ';' or '?'
179 separator = url[-1] == '?' and ';' or '?'
134 for name, value in fields:
180 for name, value in fields:
135 yield dict(name=name, value=value, separator=separator)
181 yield dict(name=name, value=value, separator=separator)
136 separator = ';'
182 separator = ';'
137
183
138 rows = []
184 rows = []
139 parity = paritygen(self.stripecount)
185 parity = paritygen(self.stripecount)
140 for name, path in self.repos:
186 for name, path in self.repos:
141 if not name.startswith(subdir):
187 if not name.startswith(subdir):
142 continue
188 continue
143 name = name[len(subdir):]
189 name = name[len(subdir):]
144
190
145 u = ui.ui(parentui=parentui)
191 u = ui.ui(parentui=self.parentui)
146 try:
192 try:
147 u.readconfig(os.path.join(path, '.hg', 'hgrc'))
193 u.readconfig(os.path.join(path, '.hg', 'hgrc'))
148 except Exception, e:
194 except Exception, e:
149 u.warn(_('error reading %s/.hg/hgrc: %s\n' % (path, e)))
195 u.warn(_('error reading %s/.hg/hgrc: %s\n' % (path, e)))
150 continue
196 continue
151 def get(section, name, default=None):
197 def get(section, name, default=None):
152 return u.config(section, name, default, untrusted=True)
198 return u.config(section, name, default, untrusted=True)
153
199
154 if u.configbool("web", "hidden", untrusted=True):
200 if u.configbool("web", "hidden", untrusted=True):
155 continue
201 continue
156
202
157 parts = [req.env['PATH_INFO'], name]
203 parts = [req.env['PATH_INFO'], name]
158 if req.env['SCRIPT_NAME']:
204 if req.env['SCRIPT_NAME']:
159 parts.insert(0, req.env['SCRIPT_NAME'])
205 parts.insert(0, req.env['SCRIPT_NAME'])
160 url = ('/'.join(parts).replace("//", "/")) + '/'
206 url = ('/'.join(parts).replace("//", "/")) + '/'
161
207
162 # update time with local timezone
208 # update time with local timezone
163 try:
209 try:
164 d = (get_mtime(path), util.makedate()[1])
210 d = (get_mtime(path), util.makedate()[1])
165 except OSError:
211 except OSError:
166 continue
212 continue
167
213
168 contact = (get("ui", "username") or # preferred
214 contact = (get("ui", "username") or # preferred
169 get("web", "contact") or # deprecated
215 get("web", "contact") or # deprecated
170 get("web", "author", "")) # also
216 get("web", "author", "")) # also
171 description = get("web", "description", "")
217 description = get("web", "description", "")
172 name = get("web", "name", name)
218 name = get("web", "name", name)
173 row = dict(contact=contact or "unknown",
219 row = dict(contact=contact or "unknown",
174 contact_sort=contact.upper() or "unknown",
220 contact_sort=contact.upper() or "unknown",
175 name=name,
221 name=name,
176 name_sort=name,
222 name_sort=name,
177 url=url,
223 url=url,
178 description=description or "unknown",
224 description=description or "unknown",
179 description_sort=description.upper() or "unknown",
225 description_sort=description.upper() or "unknown",
180 lastchange=d,
226 lastchange=d,
181 lastchange_sort=d[1]-d[0],
227 lastchange_sort=d[1]-d[0],
182 sessionvars=sessionvars,
228 sessionvars=sessionvars,
183 archives=archivelist(u, "tip", url))
229 archives=archivelist(u, "tip", url))
184 if (not sortcolumn
230 if (not sortcolumn
185 or (sortcolumn, descending) == self.repos_sorted):
231 or (sortcolumn, descending) == self.repos_sorted):
186 # fast path for unsorted output
232 # fast path for unsorted output
187 row['parity'] = parity.next()
233 row['parity'] = parity.next()
188 yield row
234 yield row
189 else:
235 else:
190 rows.append((row["%s_sort" % sortcolumn], row))
236 rows.append((row["%s_sort" % sortcolumn], row))
191 if rows:
237 if rows:
192 rows.sort()
238 rows.sort()
193 if descending:
239 if descending:
194 rows.reverse()
240 rows.reverse()
195 for key, row in rows:
241 for key, row in rows:
196 row['parity'] = parity.next()
242 row['parity'] = parity.next()
197 yield row
243 yield row
198
244
199 def makeindex(req, subdir=""):
245 sortable = ["name", "description", "contact", "lastchange"]
200 sortable = ["name", "description", "contact", "lastchange"]
246 sortcolumn, descending = self.repos_sorted
201 sortcolumn, descending = self.repos_sorted
247 if req.form.has_key('sort'):
202 if req.form.has_key('sort'):
248 sortcolumn = req.form['sort'][0]
203 sortcolumn = req.form['sort'][0]
249 descending = sortcolumn.startswith('-')
204 descending = sortcolumn.startswith('-')
250 if descending:
205 if descending:
251 sortcolumn = sortcolumn[1:]
206 sortcolumn = sortcolumn[1:]
252 if sortcolumn not in sortable:
207 if sortcolumn not in sortable:
253 sortcolumn = ""
208 sortcolumn = ""
209
210 sort = [("sort_%s" % column,
211 "%s%s" % ((not descending and column == sortcolumn)
212 and "-" or "", column))
213 for column in sortable]
214 req.write(tmpl("index", entries=entries, subdir=subdir,
215 sortcolumn=sortcolumn, descending=descending,
216 **dict(sort)))
217
254
218 try:
255 sort = [("sort_%s" % column,
219 try:
256 "%s%s" % ((not descending and column == sortcolumn)
220 virtual = req.env.get("PATH_INFO", "").strip('/')
257 and "-" or "", column))
221 if virtual.startswith('static/'):
258 for column in sortable]
222 static = os.path.join(templater.templatepath(), 'static')
259 req.write(tmpl("index", entries=entries, subdir=subdir,
223 fname = virtual[7:]
260 sortcolumn=sortcolumn, descending=descending,
224 req.write(staticfile(static, fname, req))
261 **dict(sort)))
225 elif virtual:
226 repos = dict(self.repos)
227 while virtual:
228 real = repos.get(virtual)
229 if real:
230 req.env['REPO_NAME'] = virtual
231 try:
232 repo = hg.repository(parentui, real)
233 hgweb(repo).run_wsgi(req)
234 return
235 except IOError, inst:
236 raise ErrorResponse(500, inst.strerror)
237 except hg.RepoError, inst:
238 raise ErrorResponse(500, str(inst))
239
240 # browse subdirectories
241 subdir = virtual + '/'
242 if [r for r in repos if r.startswith(subdir)]:
243 makeindex(req, subdir)
244 return
245
246 up = virtual.rfind('/')
247 if up < 0:
248 break
249 virtual = virtual[:up]
250
251 req.respond(404, tmpl("notfound", repo=virtual))
252 else:
253 if req.form.has_key('static'):
254 static = os.path.join(templater.templatepath(), "static")
255 fname = req.form['static'][0]
256 req.write(staticfile(static, fname, req))
257 else:
258 makeindex(req)
259 except ErrorResponse, err:
260 req.respond(err.code, tmpl('error', error=err.message or ''))
261 finally:
262 tmpl = None
@@ -1,77 +1,84 b''
1 #!/bin/sh
1 #!/bin/sh
2 # Tests some basic hgwebdir functionality. Tests setting up paths and
2 # Tests some basic hgwebdir functionality. Tests setting up paths and
3 # collection, different forms of 404s and the subdirectory support.
3 # collection, different forms of 404s and the subdirectory support.
4
4
5 mkdir webdir
5 mkdir webdir
6 cd webdir
6 cd webdir
7
7
8 hg init a
8 hg init a
9 echo a > a/a
9 echo a > a/a
10 hg --cwd a ci -Ama -d'1 0'
10 hg --cwd a ci -Ama -d'1 0'
11
11
12 hg init b
12 hg init b
13 echo b > b/b
13 echo b > b/b
14 hg --cwd b ci -Amb -d'2 0'
14 hg --cwd b ci -Amb -d'2 0'
15
15
16 hg init c
16 hg init c
17 echo c > c/c
17 echo c > c/c
18 hg --cwd c ci -Amc -d'3 0'
18 hg --cwd c ci -Amc -d'3 0'
19 root=`pwd`
19 root=`pwd`
20
20
21 cd ..
21 cd ..
22
22
23 cat > paths.conf <<EOF
23 cat > paths.conf <<EOF
24 [paths]
24 [paths]
25 a=$root/a
25 a=$root/a
26 b=$root/b
26 b=$root/b
27 EOF
27 EOF
28
28
29 hg serve -p $HGPORT -d --pid-file=hg.pid --webdir-conf paths.conf \
29 hg serve -p $HGPORT -d --pid-file=hg.pid --webdir-conf paths.conf \
30 -A access-paths.log -E error-paths.log
30 -A access-paths.log -E error-paths-1.log
31 cat hg.pid >> $DAEMON_PIDS
31 cat hg.pid >> $DAEMON_PIDS
32
32
33 echo % should give a 404 - file does not exist
33 echo % should give a 404 - file does not exist
34 "$TESTDIR/get-with-headers.py" localhost:$HGPORT '/a/file/tip/bork?style=raw'
34 "$TESTDIR/get-with-headers.py" localhost:$HGPORT '/a/file/tip/bork?style=raw'
35
35
36 echo % should succeed
36 echo % should succeed
37 "$TESTDIR/get-with-headers.py" localhost:$HGPORT '/?style=raw'
37 "$TESTDIR/get-with-headers.py" localhost:$HGPORT '/?style=raw'
38 "$TESTDIR/get-with-headers.py" localhost:$HGPORT '/a/file/tip/a?style=raw'
38 "$TESTDIR/get-with-headers.py" localhost:$HGPORT '/a/file/tip/a?style=raw'
39 "$TESTDIR/get-with-headers.py" localhost:$HGPORT '/b/file/tip/b?style=raw'
39 "$TESTDIR/get-with-headers.py" localhost:$HGPORT '/b/file/tip/b?style=raw'
40
40
41 echo % should give a 404 - repo is not published
41 echo % should give a 404 - repo is not published
42 "$TESTDIR/get-with-headers.py" localhost:$HGPORT '/c/file/tip/c?style=raw'
42 "$TESTDIR/get-with-headers.py" localhost:$HGPORT '/c/file/tip/c?style=raw'
43
43
44 cat > paths.conf <<EOF
44 cat > paths.conf <<EOF
45 [paths]
45 [paths]
46 t/a/=$root/a
46 t/a/=$root/a
47 b=$root/b
47 b=$root/b
48 EOF
48 EOF
49
49
50 hg serve -p $HGPORT1 -d --pid-file=hg.pid --webdir-conf paths.conf \
50 hg serve -p $HGPORT1 -d --pid-file=hg.pid --webdir-conf paths.conf \
51 -A access-paths.log -E error-paths.log
51 -A access-paths.log -E error-paths-2.log
52 cat hg.pid >> $DAEMON_PIDS
52 cat hg.pid >> $DAEMON_PIDS
53
53
54 echo % should succeed, slashy names
54 echo % should succeed, slashy names
55 "$TESTDIR/get-with-headers.py" localhost:$HGPORT1 '/?style=raw'
55 "$TESTDIR/get-with-headers.py" localhost:$HGPORT1 '/?style=raw'
56 "$TESTDIR/get-with-headers.py" localhost:$HGPORT1 '/t?style=raw'
56 "$TESTDIR/get-with-headers.py" localhost:$HGPORT1 '/t?style=raw'
57 "$TESTDIR/get-with-headers.py" localhost:$HGPORT1 '/t/?style=raw'
57 "$TESTDIR/get-with-headers.py" localhost:$HGPORT1 '/t/?style=raw'
58 "$TESTDIR/get-with-headers.py" localhost:$HGPORT1 '/t/a?style=atom' \
58 "$TESTDIR/get-with-headers.py" localhost:$HGPORT1 '/t/a?style=atom' \
59 | sed "s/http:\/\/[^/]*\//http:\/\/127.0.0.1\//"
59 | sed "s/http:\/\/[^/]*\//http:\/\/127.0.0.1\//"
60 "$TESTDIR/get-with-headers.py" localhost:$HGPORT1 '/t/a/?style=atom' \
60 "$TESTDIR/get-with-headers.py" localhost:$HGPORT1 '/t/a/?style=atom' \
61 | sed "s/http:\/\/[^/]*\//http:\/\/127.0.0.1\//"
61 | sed "s/http:\/\/[^/]*\//http:\/\/127.0.0.1\//"
62 "$TESTDIR/get-with-headers.py" localhost:$HGPORT1 '/t/a/file/tip/a?style=raw'
62 "$TESTDIR/get-with-headers.py" localhost:$HGPORT1 '/t/a/file/tip/a?style=raw'
63
63
64 cat > collections.conf <<EOF
64 cat > collections.conf <<EOF
65 [collections]
65 [collections]
66 $root=$root
66 $root=$root
67 EOF
67 EOF
68
68
69 hg serve -p $HGPORT2 -d --pid-file=hg.pid --webdir-conf collections.conf \
69 hg serve -p $HGPORT2 -d --pid-file=hg.pid --webdir-conf collections.conf \
70 -A access-collections.log -E error-collections.log
70 -A access-collections.log -E error-collections.log
71 cat hg.pid >> $DAEMON_PIDS
71 cat hg.pid >> $DAEMON_PIDS
72
72
73 echo % should succeed
73 echo % should succeed
74 "$TESTDIR/get-with-headers.py" localhost:$HGPORT2 '/?style=raw'
74 "$TESTDIR/get-with-headers.py" localhost:$HGPORT2 '/?style=raw'
75 "$TESTDIR/get-with-headers.py" localhost:$HGPORT2 '/a/file/tip/a?style=raw'
75 "$TESTDIR/get-with-headers.py" localhost:$HGPORT2 '/a/file/tip/a?style=raw'
76 "$TESTDIR/get-with-headers.py" localhost:$HGPORT2 '/b/file/tip/b?style=raw'
76 "$TESTDIR/get-with-headers.py" localhost:$HGPORT2 '/b/file/tip/b?style=raw'
77 "$TESTDIR/get-with-headers.py" localhost:$HGPORT2 '/c/file/tip/c?style=raw'
77 "$TESTDIR/get-with-headers.py" localhost:$HGPORT2 '/c/file/tip/c?style=raw'
78
79 echo % paths errors 1
80 cat error-paths-1.log
81 echo % paths errors 2
82 cat error-paths-2.log
83 echo % collections errors
84 cat error-collections.log
@@ -1,121 +1,124 b''
1 adding a
1 adding a
2 adding b
2 adding b
3 adding c
3 adding c
4 % should give a 404 - file does not exist
4 % should give a 404 - file does not exist
5 404 Not Found
5 404 Not Found
6
6
7
7
8 error: Path not found: bork/
8 error: Path not found: bork/
9 % should succeed
9 % should succeed
10 200 Script output follows
10 200 Script output follows
11
11
12
12
13 /a/
13 /a/
14 /b/
14 /b/
15
15
16 200 Script output follows
16 200 Script output follows
17
17
18 a
18 a
19 200 Script output follows
19 200 Script output follows
20
20
21 b
21 b
22 % should give a 404 - repo is not published
22 % should give a 404 - repo is not published
23 404 Not Found
23 404 Not Found
24
24
25
25
26 error: repository c not found
26 error: repository c not found
27 % should succeed, slashy names
27 % should succeed, slashy names
28 200 Script output follows
28 200 Script output follows
29
29
30
30
31 /b/
31 /b/
32 /t/a/
32 /t/a/
33
33
34 200 Script output follows
34 200 Script output follows
35
35
36
36
37 /t/a/
37 /t/a/
38
38
39 200 Script output follows
39 200 Script output follows
40
40
41
41
42 /t/a/
42 /t/a/
43
43
44 200 Script output follows
44 200 Script output follows
45
45
46 <?xml version="1.0" encoding="ascii"?>
46 <?xml version="1.0" encoding="ascii"?>
47 <feed xmlns="http://127.0.0.1/2005/Atom">
47 <feed xmlns="http://127.0.0.1/2005/Atom">
48 <!-- Changelog -->
48 <!-- Changelog -->
49 <id>http://127.0.0.1/t/a/</id>
49 <id>http://127.0.0.1/t/a/</id>
50 <link rel="self" href="http://127.0.0.1/t/a/atom-log"/>
50 <link rel="self" href="http://127.0.0.1/t/a/atom-log"/>
51 <link rel="alternate" href="http://127.0.0.1/t/a/"/>
51 <link rel="alternate" href="http://127.0.0.1/t/a/"/>
52 <title>t/a Changelog</title>
52 <title>t/a Changelog</title>
53 <updated>1970-01-01T00:00:01+00:00</updated>
53 <updated>1970-01-01T00:00:01+00:00</updated>
54
54
55 <entry>
55 <entry>
56 <title>a</title>
56 <title>a</title>
57 <id>http://127.0.0.1/mercurial/#changeset-8580ff50825a50c8f716709acdf8de0deddcd6ab</id>
57 <id>http://127.0.0.1/mercurial/#changeset-8580ff50825a50c8f716709acdf8de0deddcd6ab</id>
58 <link href="http://127.0.0.1/t/a/rev/8580ff50825a50c8f716709acdf8de0deddcd6ab"/>
58 <link href="http://127.0.0.1/t/a/rev/8580ff50825a50c8f716709acdf8de0deddcd6ab"/>
59 <author>
59 <author>
60 <name>test</name>
60 <name>test</name>
61 <email>&#116;&#101;&#115;&#116;</email>
61 <email>&#116;&#101;&#115;&#116;</email>
62 </author>
62 </author>
63 <updated>1970-01-01T00:00:01+00:00</updated>
63 <updated>1970-01-01T00:00:01+00:00</updated>
64 <published>1970-01-01T00:00:01+00:00</published>
64 <published>1970-01-01T00:00:01+00:00</published>
65 <content type="xhtml">
65 <content type="xhtml">
66 <div xmlns="http://127.0.0.1/1999/xhtml">
66 <div xmlns="http://127.0.0.1/1999/xhtml">
67 <pre xml:space="preserve">a</pre>
67 <pre xml:space="preserve">a</pre>
68 </div>
68 </div>
69 </content>
69 </content>
70 </entry>
70 </entry>
71
71
72 </feed>
72 </feed>
73 200 Script output follows
73 200 Script output follows
74
74
75 <?xml version="1.0" encoding="ascii"?>
75 <?xml version="1.0" encoding="ascii"?>
76 <feed xmlns="http://127.0.0.1/2005/Atom">
76 <feed xmlns="http://127.0.0.1/2005/Atom">
77 <!-- Changelog -->
77 <!-- Changelog -->
78 <id>http://127.0.0.1/t/a/</id>
78 <id>http://127.0.0.1/t/a/</id>
79 <link rel="self" href="http://127.0.0.1/t/a/atom-log"/>
79 <link rel="self" href="http://127.0.0.1/t/a/atom-log"/>
80 <link rel="alternate" href="http://127.0.0.1/t/a/"/>
80 <link rel="alternate" href="http://127.0.0.1/t/a/"/>
81 <title>t/a Changelog</title>
81 <title>t/a Changelog</title>
82 <updated>1970-01-01T00:00:01+00:00</updated>
82 <updated>1970-01-01T00:00:01+00:00</updated>
83
83
84 <entry>
84 <entry>
85 <title>a</title>
85 <title>a</title>
86 <id>http://127.0.0.1/mercurial/#changeset-8580ff50825a50c8f716709acdf8de0deddcd6ab</id>
86 <id>http://127.0.0.1/mercurial/#changeset-8580ff50825a50c8f716709acdf8de0deddcd6ab</id>
87 <link href="http://127.0.0.1/t/a/rev/8580ff50825a50c8f716709acdf8de0deddcd6ab"/>
87 <link href="http://127.0.0.1/t/a/rev/8580ff50825a50c8f716709acdf8de0deddcd6ab"/>
88 <author>
88 <author>
89 <name>test</name>
89 <name>test</name>
90 <email>&#116;&#101;&#115;&#116;</email>
90 <email>&#116;&#101;&#115;&#116;</email>
91 </author>
91 </author>
92 <updated>1970-01-01T00:00:01+00:00</updated>
92 <updated>1970-01-01T00:00:01+00:00</updated>
93 <published>1970-01-01T00:00:01+00:00</published>
93 <published>1970-01-01T00:00:01+00:00</published>
94 <content type="xhtml">
94 <content type="xhtml">
95 <div xmlns="http://127.0.0.1/1999/xhtml">
95 <div xmlns="http://127.0.0.1/1999/xhtml">
96 <pre xml:space="preserve">a</pre>
96 <pre xml:space="preserve">a</pre>
97 </div>
97 </div>
98 </content>
98 </content>
99 </entry>
99 </entry>
100
100
101 </feed>
101 </feed>
102 200 Script output follows
102 200 Script output follows
103
103
104 a
104 a
105 % should succeed
105 % should succeed
106 200 Script output follows
106 200 Script output follows
107
107
108
108
109 /a/
109 /a/
110 /b/
110 /b/
111 /c/
111 /c/
112
112
113 200 Script output follows
113 200 Script output follows
114
114
115 a
115 a
116 200 Script output follows
116 200 Script output follows
117
117
118 b
118 b
119 200 Script output follows
119 200 Script output follows
120
120
121 c
121 c
122 % paths errors 1
123 % paths errors 2
124 % collections errors
General Comments 0
You need to be logged in to leave comments. Login now