pushlog: add better tests for HTML output; r=glandium, bkero
authorGregory Szorc <gps@mozilla.com>
Mon, 29 Sep 2014 17:06:54 -0700
changeset 359940 b3d154e22d9e689728d2d5cc091cddc3b98abe88
parent 359939 d340711f1e5abff86232bfade4368e4f29c0f9f7
child 359941 7f3fb8d535106d3d95932403b4290e9b24e2e8a4
push id16998
push userrwood@mozilla.com
push dateMon, 02 May 2016 19:42:03 +0000
reviewersglandium, bkero
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.
hgext/pushlog-legacy/tests/helpers.sh
hgext/pushlog-legacy/tests/test-pushloghtml.t
hgtemplates/gitweb_ecma/map
hgtemplates/gitweb_mozilla/map
hgtemplates/gitweb_mozilla_pvt/map
test-requirements.txt
testing/http-request.py
testing/xmldump.py
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 &mdash; 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 &mdash; 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 &mdash; 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 &mdash; 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 &mdash; 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 &mdash; 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 &mdash; 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} &mdash; {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} &mdash; {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>&nbsp;|&nbsp;<a href="{url}diff/{node|short}/{file|urlescape}{sessionvars%urlparameter}">diff</a>&nbsp;|&nbsp;<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} &mdash; {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} &mdash; {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>&nbsp;|&nbsp;<a href="{url}diff/{node|short}/{file|urlescape}{sessionvars%urlparameter}">diff</a>&nbsp;|&nbsp;<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} &mdash; {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} &mdash; {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>&nbsp;|&nbsp;<a href="{url}diff/{node|short}/{file|urlescape}{sessionvars%urlparameter}">diff</a>&nbsp;|&nbsp;<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:]))