~launchpad-pqm/launchpad/devel

9678.20.2 by Max Bowsher
Here a shebang, there a shebang, everywhere a shebang!
1
#!/usr/bin/python2.5
8687.15.10 by Karl Fogel
Add the copyright header block to top-level files.
2
#
3
# Copyright 2009 Canonical Ltd.  This software is licensed under the
4
# GNU Affero General Public License version 3 (see the file LICENSE).
1102 by Canonical.com Patch Queue Manager
Lucille had some XXXs which should have been NOTEs
5
6
"""Tests that get run automatically on a merge."""
9722.1.1 by Gary Poster
[testfix][r=salgado][ui=none] Try to fix the buildbot problem: buildbot is starting test_on_merge in a way that no-one else is.
7
import _pythonpath
1102 by Canonical.com Patch Queue Manager
Lucille had some XXXs which should have been NOTEs
8
6393.2.2 by Joey Stanford
working updates since I had to change the line for pyflakes
9
import sys, time
10
import os, errno
1102 by Canonical.com Patch Queue Manager
Lucille had some XXXs which should have been NOTEs
11
import tabnanny
12
from StringIO import StringIO
5821.2.85 by James Henstridge
Add "make check_launchpad_storm_on_merge" target that runs the tests
13
import psycopg2
3308.1.2 by Stuart Bishop
Make test_on_merge.py do incremental output
14
from subprocess import Popen, PIPE, STDOUT
2083 by Canonical.com Patch Queue Manager
[r=jamesh] testrunner improvements (?)
15
from signal import SIGKILL, SIGTERM
16
from select import select
17
6393.2.1 by Joey Stanford
change the rather long timeout setting to something more reasonable and cleanup the explaination
18
# The TIMEOUT setting (expressed in seconds) affects how long a test will run
19
# before it is deemed to be hung, and then appropriately terminated.
20
# It's principal use is preventing a PQM job from hanging indefinitely and
21
# backing up the queue.
22
# e.g. Usage: TIMEOUT = 60 * 15
23
# This will set the timeout to 15 minutes.
24
TIMEOUT = 60 * 15
1102 by Canonical.com Patch Queue Manager
Lucille had some XXXs which should have been NOTEs
25
26
def main():
8234.1.4 by Gary Poster
test works, nominally; and bin/py is a bit more functional. problems with import warnings are more serious because they cause tests to fail.
27
    """Call bin/test with whatever arguments this script was run with.
1102 by Canonical.com Patch Queue Manager
Lucille had some XXXs which should have been NOTEs
28
29
    If the tests ran ok (last line of stderr is 'OK<return>') then suppress
30
    output and exit(0).
31
32
    Otherwise, print output and exit(1).
33
    """
34
    here = os.path.dirname(os.path.realpath(__file__))
35
36
    # Tabnanny
6393.2.2 by Joey Stanford
working updates since I had to change the line for pyflakes
37
    # NB. If tabnanny raises an exception, run
9678.20.3 by Max Bowsher
Final round of Python version updates 2.4 -> 2.5, in non-shebang contexts.
38
    # python /usr/lib/python2.5/tabnanny.py -vv lib/canonical
2976.10.114 by Stuart Bishop
Test tweaks
39
    # for more detailed output.
1102 by Canonical.com Patch Queue Manager
Lucille had some XXXs which should have been NOTEs
40
    org_stdout = sys.stdout
41
    sys.stdout = StringIO()
42
    tabnanny.check(os.path.join(here, 'lib', 'canonical'))
43
    tabnanny_results = sys.stdout.getvalue()
44
    sys.stdout = org_stdout
45
    if len(tabnanny_results) > 0:
46
        print '---- tabnanny bitching ----'
47
        print tabnanny_results
48
        print '---- end tabnanny bitching ----'
49
        return 1
50
1064.1.3 by James Henstridge
merge from marius
51
    # Sanity check PostgreSQL version. No point in trying to create a test
52
    # database when PostgreSQL is too old.
5821.2.85 by James Henstridge
Add "make check_launchpad_storm_on_merge" target that runs the tests
53
    con = psycopg2.connect('dbname=template1')
1064.1.3 by James Henstridge
merge from marius
54
    cur = con.cursor()
55
    cur.execute('show server_version')
56
    server_version = cur.fetchone()[0]
57
    try:
58
        numeric_server_version = tuple(map(int, server_version.split('.')))
59
    except ValueError:
60
        # Skip this check if the version number is more complicated than
61
        # we expected.
62
        pass
63
    else:
3257.1.1 by Stuart Bishop
PostgreSQL 8.0+ required
64
        if numeric_server_version < (8, 0):
65
            print 'Your PostgreSQL version is too old.  You need 8.x.x'
1064.1.3 by James Henstridge
merge from marius
66
            print 'You have %s' % server_version
67
            return 1
68
1102 by Canonical.com Patch Queue Manager
Lucille had some XXXs which should have been NOTEs
69
    # Drop the template database if it exists - the Makefile does this
70
    # too, but we can explicity check for errors here
5821.2.85 by James Henstridge
Add "make check_launchpad_storm_on_merge" target that runs the tests
71
    con = psycopg2.connect('dbname=template1')
2083 by Canonical.com Patch Queue Manager
[r=jamesh] testrunner improvements (?)
72
    con.set_isolation_level(0)
1102 by Canonical.com Patch Queue Manager
Lucille had some XXXs which should have been NOTEs
73
    cur = con.cursor()
1064.1.3 by James Henstridge
merge from marius
74
    try:
2083 by Canonical.com Patch Queue Manager
[r=jamesh] testrunner improvements (?)
75
        cur.execute('drop database launchpad_ftest_template')
5821.2.85 by James Henstridge
Add "make check_launchpad_storm_on_merge" target that runs the tests
76
    except psycopg2.ProgrammingError, x:
1520 by Canonical.com Patch Queue Manager
Review and fix database security update code
77
        if 'does not exist' not in str(x):
78
            raise
2083 by Canonical.com Patch Queue Manager
[r=jamesh] testrunner improvements (?)
79
    cur.execute("""
80
        select count(*) from pg_stat_activity
81
        where datname in ('launchpad_dev',
82
            'launchpad_ftest_template', 'launchpad_ftest')
83
        """)
84
    existing_connections = cur.fetchone()[0]
85
    if existing_connections > 0:
86
        print 'Cannot rebuild database. There are %d open connections.' % (
87
                existing_connections,
88
                )
89
        return 1
1102 by Canonical.com Patch Queue Manager
Lucille had some XXXs which should have been NOTEs
90
    cur.close()
91
    con.close()
92
93
    # Build the template database. Tests duplicate this.
94
    here = os.path.dirname(os.path.realpath(__file__))
95
    schema_dir = os.path.join(here, 'database', 'schema')
1297 by Canonical.com Patch Queue Manager
Merge in database security branch
96
    if os.system('cd %s; make test PYTHON=%s > /dev/null' % (
1155 by Canonical.com Patch Queue Manager
More robust authentication
97
        schema_dir, sys.executable)) != 0:
1764 by Canonical.com Patch Queue Manager
Now make check fails if anything goes wrong when loading the sampledata. r=stub
98
        print 'Failed to create database or load sampledata.'
1102 by Canonical.com Patch Queue Manager
Lucille had some XXXs which should have been NOTEs
99
        return 1
100
101
    # Sanity check the database. No point running tests if the
102
    # bedrock is crumbling.
5821.2.85 by James Henstridge
Add "make check_launchpad_storm_on_merge" target that runs the tests
103
    con = psycopg2.connect('dbname=launchpad_ftest_template')
1102 by Canonical.com Patch Queue Manager
Lucille had some XXXs which should have been NOTEs
104
    cur = con.cursor()
105
    cur.execute('show search_path')
106
    search_path = cur.fetchone()[0]
107
    if search_path != '$user,public,ts2':
108
        print 'Search path incorrect.'
109
        print 'Add the following line to /etc/postgresql/postgresql.conf:'
110
        print "    search_path = '$user,public,ts2'"
1064.1.3 by James Henstridge
merge from marius
111
        print "and tell postgresql to reload its configuration file."
1102 by Canonical.com Patch Queue Manager
Lucille had some XXXs which should have been NOTEs
112
        return 1
113
    cur.execute("""
114
        select pg_encoding_to_char(encoding) as encoding from pg_database
115
        where datname='launchpad_ftest_template'
116
        """)
117
    enc = cur.fetchone()[0]
3242.1.5 by Stuart Bishop
Make test_on_merge.py work with PostgreSQL 8.1
118
    if enc not in ('UNICODE', 'UTF8'):
1102 by Canonical.com Patch Queue Manager
Lucille had some XXXs which should have been NOTEs
119
        print 'Database encoding incorrectly set'
120
        return 1
1257 by Canonical.com Patch Queue Manager
Improve database locale checks, add locale sanity check to test_on_merge.py and improve test_on_merge output
121
    cur.execute(r"""
122
        SELECT setting FROM pg_settings
123
        WHERE context='internal' AND name='lc_ctype'
124
        """)
125
    loc = cur.fetchone()[0]
2083 by Canonical.com Patch Queue Manager
[r=jamesh] testrunner improvements (?)
126
    #if not (loc.startswith('en_') or loc in ('C', 'en')):
127
    if loc != 'C':
1257 by Canonical.com Patch Queue Manager
Improve database locale checks, add locale sanity check to test_on_merge.py and improve test_on_merge output
128
        print 'Database locale incorrectly set. Need to rerun initdb.'
129
        return 1
130
1102 by Canonical.com Patch Queue Manager
Lucille had some XXXs which should have been NOTEs
131
    # Explicity close our connections - things will fail if we leave open
132
    # connections.
133
    cur.close()
134
    del cur
135
    con.close()
136
    del con
137
138
    print 'Running tests.'
2083 by Canonical.com Patch Queue Manager
[r=jamesh] testrunner improvements (?)
139
    os.chdir(here)
8234.1.4 by Gary Poster
test works, nominally; and bin/py is a bit more functional. problems with import warnings are more serious because they cause tests to fail.
140
    cmd = [os.path.join(here, 'bin', 'test')] + sys.argv[1:]
2083 by Canonical.com Patch Queue Manager
[r=jamesh] testrunner improvements (?)
141
    print ' '.join(cmd)
4107.1.9 by Stuart Bishop
Increase test_on_merge timeout to one hour
142
143
    # Run the test suite and return the error code
144
    #return call(cmd)
145
3308.1.2 by Stuart Bishop
Make test_on_merge.py do incremental output
146
    proc = Popen(cmd, stdin=PIPE, stdout=PIPE, stderr=STDOUT)
2083 by Canonical.com Patch Queue Manager
[r=jamesh] testrunner improvements (?)
147
    proc.stdin.close()
148
3258.1.1 by Andrew Bennetts
Fix backwards timeout detection logic in test_on_merge.py
149
    # Do proc.communicate(), but timeout if there's no activity on stdout or
150
    # stderr for too long.
3308.1.2 by Stuart Bishop
Make test_on_merge.py do incremental output
151
    open_readers = set([proc.stdout])
3308.1.4 by Stuart Bishop
Fix test_on_merge.py incremental output
152
    while open_readers:
2083 by Canonical.com Patch Queue Manager
[r=jamesh] testrunner improvements (?)
153
        rlist, wlist, xlist = select(open_readers, [], [], TIMEOUT)
154
4092.2.18 by Barry Warsaw
Respond to spiv's review:
155
        if len(rlist) == 0:
3258.1.1 by Andrew Bennetts
Fix backwards timeout detection logic in test_on_merge.py
156
            if proc.poll() is not None:
2083 by Canonical.com Patch Queue Manager
[r=jamesh] testrunner improvements (?)
157
                break
6393.2.2 by Joey Stanford
working updates since I had to change the line for pyflakes
158
            print ("\nA test appears to be hung. There has been no output for"
159
                " %d seconds. Sending SIGTERM." % TIMEOUT)
2083 by Canonical.com Patch Queue Manager
[r=jamesh] testrunner improvements (?)
160
            killem(proc.pid, SIGTERM)
161
            time.sleep(3)
3258.1.1 by Andrew Bennetts
Fix backwards timeout detection logic in test_on_merge.py
162
            if proc.poll() is not None:
6393.2.2 by Joey Stanford
working updates since I had to change the line for pyflakes
163
                print ("\nSIGTERM did not work. Sending SIGKILL.")
2083 by Canonical.com Patch Queue Manager
[r=jamesh] testrunner improvements (?)
164
                killem(proc.pid, SIGKILL)
4092.2.12 by Barry Warsaw
Thanks to jamesh, do a better job of draining the subprocess's stdout and
165
            # Drain the subprocess's stdout and stderr.
166
            sys.stdout.write(proc.stdout.read())
167
            break
2083 by Canonical.com Patch Queue Manager
[r=jamesh] testrunner improvements (?)
168
169
        if proc.stdout in rlist:
3308.1.2 by Stuart Bishop
Make test_on_merge.py do incremental output
170
            chunk = os.read(proc.stdout.fileno(), 1024)
3367.1.2 by Stuart Bishop
Don't insert random spaces in test_on_merge.py output
171
            sys.stdout.write(chunk)
3308.1.2 by Stuart Bishop
Make test_on_merge.py do incremental output
172
            if chunk == "":
3308.1.4 by Stuart Bishop
Fix test_on_merge.py incremental output
173
                open_readers.remove(proc.stdout)
3308.1.2 by Stuart Bishop
Make test_on_merge.py do incremental output
174
3308.1.4 by Stuart Bishop
Fix test_on_merge.py incremental output
175
    rv = proc.wait()
3308.1.2 by Stuart Bishop
Make test_on_merge.py do incremental output
176
    if rv == 0:
6393.2.2 by Joey Stanford
working updates since I had to change the line for pyflakes
177
        print '\nSuccessfully ran all tests.'
1102 by Canonical.com Patch Queue Manager
Lucille had some XXXs which should have been NOTEs
178
    else:
3308.1.2 by Stuart Bishop
Make test_on_merge.py do incremental output
179
        print '\nTests failed (exit code %d)' % rv
180
181
    return rv
182
1102 by Canonical.com Patch Queue Manager
Lucille had some XXXs which should have been NOTEs
183
2083 by Canonical.com Patch Queue Manager
[r=jamesh] testrunner improvements (?)
184
def killem(pid, signal):
185
    """Kill the process group leader identified by pid and other group members
186
8234.1.4 by Gary Poster
test works, nominally; and bin/py is a bit more functional. problems with import warnings are more serious because they cause tests to fail.
187
    Note that bin/test sets its process to a process group leader.
2083 by Canonical.com Patch Queue Manager
[r=jamesh] testrunner improvements (?)
188
    """
189
    try:
190
        os.killpg(os.getpgid(pid), signal)
191
    except OSError, x:
192
        if x.errno == errno.ESRCH:
193
            pass
194
        else:
195
            raise
196
1102 by Canonical.com Patch Queue Manager
Lucille had some XXXs which should have been NOTEs
197
if __name__ == '__main__':
198
    sys.exit(main())