pushlog: add better tests for HTML output; r=glandium, bkero
Our current test coverage of pushlog HTML is not great - we simply do a
sanity test. With this patch, we start verifying the returned HTML
content is what we expect.
To facilitate testing only a subset of returned content, the templates
were modified to make identifying pushlog entries from the XML output
a bit easier.
new file mode 100644
--- /dev/null
+++ b/hgext/pushlog-legacy/tests/helpers.sh
@@ -0,0 +1,20 @@
+serverconfig() {
+ cat >> $1 << EOF
+[extensions]
+pushlog-feed = $TESTDIR/hgext/pushlog-legacy/pushlog-feed.py
+buglink = $TESTDIR/hgext/pushlog-legacy/buglink.py
+hgwebjson = $TESTDIR/hgext/pushlog-legacy/hgwebjson.py
+
+[web]
+push_ssl = False
+allow_push = *
+templates = $TESTDIR/hgtemplates
+style = gitweb_mozilla
+
+[hooks]
+pretxnchangegroup.pushlog = python:mozhghooks.pushlog.log
+
+EOF
+}
+
+alias http=$TESTDIR/testing/http-request.py
new file mode 100644
--- /dev/null
+++ b/hgext/pushlog-legacy/tests/test-pushloghtml.t
@@ -0,0 +1,113 @@
+ $ . $TESTDIR/hgext/pushlog-legacy/tests/helpers.sh
+ $ hg init server
+ $ cd server
+ $ serverconfig .hg/hgrc
+ $ export USER=hguser
+ $ hg serve -d -p $HGPORT --pid-file hg.pid
+ $ cat hg.pid >> $DAEMON_PIDS
+ $ cd ..
+
+Populate the repo with some data
+
+ $ hg clone http://localhost:$HGPORT client > /dev/null
+ $ cd client
+ $ touch foo
+ $ hg commit -A -m 'initial commit'
+ adding foo
+ $ hg push
+ pushing to http://localhost:$HGPORT/
+ searching for changes
+ remote: adding changesets
+ remote: adding manifests
+ remote: adding file changes
+ remote: added 1 changesets with 1 changes to 1 files
+ remote: Trying to insert into pushlog.
+ remote: Please do not interrupt...
+ remote: Inserted into the pushlog db successfully.
+
+ $ hg branch branch_foo > /dev/null
+ $ echo foo1 > foo
+ $ hg commit -m 'first commit on branch_foo'
+ $ hg push --new-branch > /dev/null
+ $ echo foo2 > foo
+ $ hg commit -m 'second commit on branch_foo'
+ $ hg tag -m 'tagging foo2' foo2
+ $ hg push > /dev/null
+
+ $ hg up -r 0 > /dev/null
+ $ hg branch branch_bar > /dev/null
+ $ echo bar1 > foo
+ $ hg commit -m 'first commit on branch_bar'
+ $ echo bar2 > foo
+ $ hg commit -m 'second commit on branch_bar'
+ $ hg tag -m 'tagging bar2' bar2
+ $ hg push --new-branch > /dev/null
+
+Pushlog HTML sanity test
+
+ $ http http://localhost:$HGPORT/pushloghtml --header content-type --no-body
+ 200
+ content-type: text/html; charset=ascii
+
+Main HTML page lists all pushes
+
+ $ http http://localhost:$HGPORT/pushloghtml --body-file body --no-headers
+ 200
+ $ python $TESTDIR/testing/xmldump.py body "//*[contains(@class, 'pushlogentry')]"
+ <tr xmlns="http://www.w3.org/1999/xhtml" class="pushlogentry parity0 id4">
+ <td>
+ <cite>hguser<br /><span class="date">*</span></cite> (glob)
+ </td>
+ <td class="age">
+ <a href="/rev/fccd2b8674ef">fccd2b8674ef</a>
+ </td>
+ <td><strong>test — tagging bar2</strong> <span class="logtags"><span class="tagtag" title="tip">tip</span> </span></td>
+ </tr>
+ <tr xmlns="http://www.w3.org/1999/xhtml" class="pushlogentry parity0 id4">
+ <td></td>
+ <td class="age">
+ <a href="/rev/b32e82060ac2">b32e82060ac2</a>
+ </td>
+ <td><strong>test — second commit on branch_bar</strong> <span class="logtags"><span class="tagtag" title="bar2">bar2</span> </span></td>
+ </tr>
+ <tr xmlns="http://www.w3.org/1999/xhtml" class="pushlogentry parity0 id4">
+ <td></td>
+ <td class="age">
+ <a href="/rev/925e1c8915ab">925e1c8915ab</a>
+ </td>
+ <td><strong>test — first commit on branch_bar</strong> <span class="logtags"></span></td>
+ </tr>
+ <tr xmlns="http://www.w3.org/1999/xhtml" class="pushlogentry parity1 id3">
+ <td>
+ <cite>hguser<br /><span class="date">*</span></cite> (glob)
+ </td>
+ <td class="age">
+ <a href="/rev/0cebc5195347">0cebc5195347</a>
+ </td>
+ <td><strong>test — tagging foo2</strong> <span class="logtags"></span></td>
+ </tr>
+ <tr xmlns="http://www.w3.org/1999/xhtml" class="pushlogentry parity1 id3">
+ <td></td>
+ <td class="age">
+ <a href="/rev/17880384fe19">17880384fe19</a>
+ </td>
+ <td><strong>test — second commit on branch_foo</strong> <span class="logtags"><span class="tagtag" title="foo2">foo2</span> </span></td>
+ </tr>
+ <tr xmlns="http://www.w3.org/1999/xhtml" class="pushlogentry parity0 id2">
+ <td>
+ <cite>hguser<br /><span class="date">*</span></cite> (glob)
+ </td>
+ <td class="age">
+ <a href="/rev/a8ffcd74ae3e">a8ffcd74ae3e</a>
+ </td>
+ <td><strong>test — first commit on branch_foo</strong> <span class="logtags"></span></td>
+ </tr>
+ <tr xmlns="http://www.w3.org/1999/xhtml" class="pushlogentry parity1 id1">
+ <td>
+ <cite>hguser<br /><span class="date">*</span></cite> (glob)
+ </td>
+ <td class="age">
+ <a href="/rev/04caf62ca417">04caf62ca417</a>
+ </td>
+ <td><strong>test — initial commit</strong> <span class="logtags"></span></td>
+ </tr>
--- a/hgtemplates/gitweb_ecma/map
+++ b/hgtemplates/gitweb_ecma/map
@@ -55,17 +55,17 @@ filelogchild = '<tr><td align="right">ch
shortlog = shortlog.tmpl
pushlog = pushlog.tmpl
tagtag = '<span class="tagtag" title="{name}">{name}</span> '
branchtag = '<span class="branchtag" title="{name}">{name}</span> '
inbranchtag = '<span class="inbranchtag" title="{name}">{name}</span> '
shortlogentry = '<tr class="parity{parity}"><td class="link"><a href="{url}rev/{node|short}{sessionvars%urlparameter}">diff</a><br/><a href="{url}file/{node|short}{sessionvars%urlparameter}">browse</a></td><td class="age">{node|short}<br/><i>{date|isodate}</i></td><td><strong><cite>{author|person}</cite> - {desc|strip|firstline|escape|buglink}</strong> <span class="logtags">{inbranch%inbranchtag}{branches%branchtag}{tags%tagtag}</span></td></tr>'
pushinfo = '<cite>{user}<br/><span class="date">{date|date}</span></cite>'
mergehidden = '<br/>← {count} hidden changesets <a class="expand hideid{id}" href="#">[Expand]</a>'
-pushlogentry = '<tr class="parity{parity} {hidden} id{id}"><td>{push%pushinfo}</td><td class="age"><a href="{url}rev/{node|short}{sessionvars%urlparameter}">{node|short}</a></td><td><strong>{author|person} — {desc|strip|escape|buglink}</strong> <span class="logtags">{inbranch%inbranchtag}{branches%branchtag}{tags%tagtag}</span>{mergerollup%mergehidden}</td></tr>\n'
+pushlogentry = '<tr class="pushlogentry parity{parity} {hidden} id{id}"><td>{push%pushinfo}</td><td class="age"><a href="{url}rev/{node|short}{sessionvars%urlparameter}">{node|short}</a></td><td><strong>{author|person} — {desc|strip|escape|buglink}</strong> <span class="logtags">{inbranch%inbranchtag}{branches%branchtag}{tags%tagtag}</span>{mergerollup%mergehidden}</td></tr>\n'
filelogentry = '<tr class="parity{parity}"><td class="link"><a href="{url}rev/{node|short}{sessionvars%urlparameter}">diff</a><br/><a href="{url}file/{node|short}{sessionvars%urlparameter}">browse</a><br/><a href="{url}annotate/{node|short}/{file|urlescape}{sessionvars%urlparameter}">annotate</a></td><td class="age">{node|short}<br/><i>{date|isodate}</i>{rename%filelogrename}</td><td><strong><cite>{author|person}</cite> - {desc|strip|firstline|escape|buglink}</strong></td></tr>'
filelogentry_old = '<tr class="parity{parity}"><td class="age"><i>at {date|date}</i></td><td><a class="list" href="{url}rev/{node|short}{sessionvars%urlparameter}"><b>{desc|strip|firstline|escape|buglink}</b></a></td><td class="link"><a href="{url}file/{node|short}/{file|urlescape}{sessionvars%urlparameter}">file</a> | <a href="{url}diff/{node|short}/{file|urlescape}{sessionvars%urlparameter}">diff</a> | <a href="{url}annotate/{node|short}/{file|urlescape}{sessionvars%urlparameter}">annotate</a> {rename%filelogrename}</td></tr>'
archiveentry = ' | <a href="{url}archive/{node|short}{extension}">{type|escape}</a> '
indexentry = '<tr class="parity{parity}"><td><a class="list" href="{url}{sessionvars%urlparameter}"><b>{name|escape}</b></a></td><td>{description}</td><td class="age">at {lastchange|date}</td><td class="indexlinks">{archives%indexarchiveentry}</td><td><a href="{url}atom-log"><img src="{staticurl}livemarks16.png" alt="Feed" title="Feed of repository changes"/></a></td></tr>\n'
indexarchiveentry = ' <a href="{url}archive/{node|short}{extension}">{type|escape}</a> '
index = index.tmpl
urlparameter = '{separator}{name}={value|urlescape}'
hiddenformentry = '<input type="hidden" name="{name}" value="{value|escape}" />'
--- a/hgtemplates/gitweb_mozilla/map
+++ b/hgtemplates/gitweb_mozilla/map
@@ -61,17 +61,17 @@ filelogchild = '<tr><td align="right">ch
shortlog = shortlog.tmpl
pushlog = pushlog.tmpl
tagtag = '<span class="tagtag" title="{name}">{name}</span> '
branchtag = '<span class="branchtag" title="{name}">{name}</span> '
inbranchtag = '<span class="inbranchtag" title="{name}">{name}</span> '
shortlogentry = '<tr class="parity{parity}"><td class="link"><a href="{url}rev/{node|short}{sessionvars%urlparameter}">diff</a><br/><a href="{url}file/{node|short}{sessionvars%urlparameter}">browse</a></td><td class="age">{node|short}<br/><i>{date|isodate}</i></td><td><strong><cite>{author|person}</cite> - {desc|strip|firstline|escape|buglink}</strong> <span class="logtags">{inbranch%inbranchtag}{branches%branchtag}{tags%tagtag}</span></td></tr>'
pushinfo = '<cite>{user}<br/><span class="date">{date|date}</span></cite>'
mergehidden = '<br/>← {count} merge changesets <a class="expand hideid{id}" href="#">[Collapse]</a>'
-pushlogentry = '<tr class="parity{parity} {hidden} id{id}"><td>{push%pushinfo}</td><td class="age"><a href="{url}rev/{node|short}{sessionvars%urlparameter}">{node|short}</a></td><td><strong>{author|person} — {desc|strip|escape|buglink}</strong> <span class="logtags">{inbranch%inbranchtag}{branches%branchtag}{tags%tagtag}</span>{mergerollup%mergehidden}</td></tr>\n'
+pushlogentry = '<tr class="pushlogentry parity{parity} {hidden} id{id}"><td>{push%pushinfo}</td><td class="age"><a href="{url}rev/{node|short}{sessionvars%urlparameter}">{node|short}</a></td><td><strong>{author|person} — {desc|strip|escape|buglink}</strong> <span class="logtags">{inbranch%inbranchtag}{branches%branchtag}{tags%tagtag}</span>{mergerollup%mergehidden}</td></tr>\n'
filelogentry = '<tr class="parity{parity}"><td class="link"><a href="{url}rev/{node|short}{sessionvars%urlparameter}">diff</a><br/><a href="{url}file/{node|short}{sessionvars%urlparameter}">browse</a><br/><a href="{url}annotate/{node|short}/{file|urlescape}{sessionvars%urlparameter}">annotate</a></td><td class="age">{node|short}<br/><i>{date|isodate}</i>{rename%filelogrename}</td><td><strong><cite>{author|person}</cite> - {desc|strip|firstline|escape|buglink}</strong></td></tr>'
filelogentry_old = '<tr class="parity{parity}"><td class="age"><i>at {date|date}</i></td><td><a class="list" href="{url}rev/{node|short}{sessionvars%urlparameter}"><b>{desc|strip|firstline|escape|buglink}</b></a></td><td class="link"><a href="{url}file/{node|short}/{file|urlescape}{sessionvars%urlparameter}">file</a> | <a href="{url}diff/{node|short}/{file|urlescape}{sessionvars%urlparameter}">diff</a> | <a href="{url}annotate/{node|short}/{file|urlescape}{sessionvars%urlparameter}">annotate</a> {rename%filelogrename}</td></tr>'
archiveentry = ' | <a href="{url}archive/{node|short}{extension}">{type|escape}</a> '
indexentry = '<tr class="parity{parity}"><td><a class="list" href="{url}{sessionvars%urlparameter}"><b>{name|escape}</b></a></td><td>{description}</td><td class="age">at {lastchange|date}</td><td class="indexlinks">{archives%indexarchiveentry}</td><td><a href="{url}atom-log"><img src="{staticurl}livemarks16.png" alt="Feed" title="Feed of repository changes"/></a></td></tr>\n'
indexarchiveentry = ' <a href="{url}archive/{node|short}{extension}">{type|escape}</a> '
index = index.tmpl
urlparameter = '{separator}{name}={value|urlescape}'
hiddenformentry = '<input type="hidden" name="{name}" value="{value|escape}" />'
--- a/hgtemplates/gitweb_mozilla_pvt/map
+++ b/hgtemplates/gitweb_mozilla_pvt/map
@@ -55,17 +55,17 @@ filelogchild = '<tr><td align="right">ch
shortlog = shortlog.tmpl
pushlog = pushlog.tmpl
tagtag = '<span class="tagtag" title="{name}">{name}</span> '
branchtag = '<span class="branchtag" title="{name}">{name}</span> '
inbranchtag = '<span class="inbranchtag" title="{name}">{name}</span> '
shortlogentry = '<tr class="parity{parity}"><td class="link"><a href="{url}rev/{node|short}{sessionvars%urlparameter}">diff</a><br/><a href="{url}file/{node|short}{sessionvars%urlparameter}">browse</a></td><td class="age">{node|short}<br/><i>{date|isodate}</i></td><td><strong><cite>{author|person}</cite> - {desc|strip|firstline|escape|buglink}</strong> <span class="logtags">{inbranch%inbranchtag}{branches%branchtag}{tags%tagtag}</span></td></tr>'
pushinfo = '<cite>{user}<br/><span class="date">{date|date}</span></cite>'
mergehidden = '<br/>← {count} merge changesets <a class="expand hideid{id}" href="#">[Collapse]</a>'
-pushlogentry = '<tr class="parity{parity} {hidden} id{id}"><td>{push%pushinfo}</td><td class="age"><a href="{url}rev/{node|short}{sessionvars%urlparameter}">{node|short}</a></td><td><strong>{author|person} — {desc|strip|escape|buglink}</strong> <span class="logtags">{inbranch%inbranchtag}{branches%branchtag}{tags%tagtag}</span>{mergerollup%mergehidden}</td></tr>\n'
+pushlogentry = '<tr class="pushlogentry parity{parity} {hidden} id{id}"><td>{push%pushinfo}</td><td class="age"><a href="{url}rev/{node|short}{sessionvars%urlparameter}">{node|short}</a></td><td><strong>{author|person} — {desc|strip|escape|buglink}</strong> <span class="logtags">{inbranch%inbranchtag}{branches%branchtag}{tags%tagtag}</span>{mergerollup%mergehidden}</td></tr>\n'
filelogentry = '<tr class="parity{parity}"><td class="link"><a href="{url}rev/{node|short}{sessionvars%urlparameter}">diff</a><br/><a href="{url}file/{node|short}{sessionvars%urlparameter}">browse</a><br/><a href="{url}annotate/{node|short}/{file|urlescape}{sessionvars%urlparameter}">annotate</a></td><td class="age">{node|short}<br/><i>{date|isodate}</i>{rename%filelogrename}</td><td><strong><cite>{author|person}</cite> - {desc|strip|firstline|escape|buglink}</strong></td></tr>'
filelogentry_old = '<tr class="parity{parity}"><td class="age"><i>at {date|date}</i></td><td><a class="list" href="{url}rev/{node|short}{sessionvars%urlparameter}"><b>{desc|strip|firstline|escape|buglink}</b></a></td><td class="link"><a href="{url}file/{node|short}/{file|urlescape}{sessionvars%urlparameter}">file</a> | <a href="{url}diff/{node|short}/{file|urlescape}{sessionvars%urlparameter}">diff</a> | <a href="{url}annotate/{node|short}/{file|urlescape}{sessionvars%urlparameter}">annotate</a> {rename%filelogrename}</td></tr>'
archiveentry = ' | <a href="{url}archive/{node|short}{extension}">{type|escape}</a> '
indexentry = '<tr class="parity{parity}"><td><a class="list" href="{url}{sessionvars%urlparameter}"><b>{name|escape}</b></a></td><td>{description}</td><td class="age">at {lastchange|date}</td><td class="indexlinks">{archives%indexarchiveentry}</td><td><a href="{url}atom-log"><img src="{staticurl}livemarks16.png" alt="Feed" title="Feed of repository changes"/></a></td></tr>\n'
indexarchiveentry = ' <a href="{url}archive/{node|short}{extension}">{type|escape}</a> '
index = index.tmpl
urlparameter = '{separator}{name}={value|urlescape}'
hiddenformentry = '<input type="hidden" name="{name}" value="{value|escape}" />'
--- a/test-requirements.txt
+++ b/test-requirements.txt
@@ -17,16 +17,17 @@ coverage==3.7.1
django-evolution==0.7.4
django-haystack==2.1.0
django-pipeline==1.3.24
docutils==0.11
docker-py==0.5.3
ecdsa==0.11
feedparser==5.1.3
futures==2.1.6
+lxml==3.4.1
mach==0.4
markupsafe==0.23
mercurial==3.2
mimeparse==0.1.3
mock==1.0.1
mozfile==1.1
mozinfo==0.7
mozprocess==0.21
new file mode 100755
--- /dev/null
+++ b/testing/http-request.py
@@ -0,0 +1,55 @@
+#!/usr/bin/env python
+# This Source Code Form is subject to the terms of the Mozilla Public
+# License, v. 2.0. If a copy of the MPL was not distributed with this
+# file, You can obtain one at http://mozilla.org/MPL/2.0/.
+
+# Script to perform an HTTP request.
+
+from __future__ import print_function
+
+import httplib
+import sys
+import urlparse
+from argparse import ArgumentParser
+
+def main(args):
+ parser = ArgumentParser()
+ parser.add_argument('--body-file',
+ help='save HTTP response body to a file')
+ parser.add_argument('--no-body', action='store_true',
+ help='Do not print HTTP response body.')
+ parser.add_argument('--header', action='append', default=[],
+ help='Display only headers in this list. Values can be comma delimited.')
+ parser.add_argument('--no-headers', action='store_true',
+ help='Do not display any header info.')
+ parser.add_argument('url',
+ help='URL to fetch')
+
+ args = parser.parse_args(args)
+ url = args.url
+
+ all_headers = not args.header
+ display_headers = set()
+ for header in args.header:
+ display_headers |= set(header.split(','))
+
+ url = urlparse.urlparse(url)
+
+ conn = httplib.HTTPConnection(url.hostname, url.port or 80)
+ conn.request('GET', '/' + url.path)
+ response = conn.getresponse()
+ print(response.status)
+
+ for header, value in response.getheaders():
+ if not args.no_headers and (all_headers or header in display_headers):
+ print('%s: %s' % (header, value))
+
+ data = response.read()
+ if args.body_file:
+ with open(args.body_file, 'wb') as fh:
+ fh.write(data)
+ elif not args.no_body:
+ print('')
+ print(data)
+
+sys.exit(main(sys.argv[1:]))
new file mode 100644
--- /dev/null
+++ b/testing/xmldump.py
@@ -0,0 +1,21 @@
+#!/usr/bin/env python
+# This Source Code Form is subject to the terms of the Mozilla Public
+# License, v. 2.0. If a copy of the MPL was not distributed with this
+# file, You can obtain one at http://mozilla.org/MPL/2.0/.
+
+# Script to extract data from XML files.
+
+import sys
+
+from lxml import etree
+
+def main(args):
+ path, xpath = args[0:2]
+
+ parser = etree.XMLParser(resolve_entities=False)
+ tree = etree.parse(path, parser=parser)
+
+ for el in tree.xpath(xpath):
+ print(etree.tostring(el, encoding='utf-8', pretty_print=True).strip())
+
+sys.exit(main(sys.argv[1:]))