~launchpad-pqm/launchpad/devel

10637.3.1 by Guilherme Salgado
Use the default python version instead of a hard-coded version
1
#!/usr/bin/python -S
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
10303.1.16 by Gary Poster
remove disgusting bit: we will change buildbot
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
10866.2.1 by Maris Fogels
Rewrote the signal handling to move the sentinal into its own process group, where it won't be killed along with its children. Move the process group control code from the testrunner into the sentinal.
15
from signal import SIGKILL, SIGTERM, SIGINT, SIGHUP
2083 by Canonical.com Patch Queue Manager
[r=jamesh] testrunner improvements (?)
16
from select import select
17
10866.2.1 by Maris Fogels
Rewrote the signal handling to move the sentinal into its own process group, where it won't be killed along with its children. Move the process group control code from the testrunner into the sentinal.
18
6393.2.1 by Joey Stanford
change the rather long timeout setting to something more reasonable and cleanup the explaination
19
# The TIMEOUT setting (expressed in seconds) affects how long a test will run
20
# before it is deemed to be hung, and then appropriately terminated.
21
# It's principal use is preventing a PQM job from hanging indefinitely and
22
# backing up the queue.
10866.2.2 by Maris Fogels
Lowered the test timeout a bit.
23
# e.g. Usage: TIMEOUT = 60 * 10
24
# This will set the timeout to 10 minutes.
25
TIMEOUT = 60 * 10
10866.2.1 by Maris Fogels
Rewrote the signal handling to move the sentinal into its own process group, where it won't be killed along with its children. Move the process group control code from the testrunner into the sentinal.
26
10866.2.7 by Maris Fogels
Split test_on_merge.py into functions, marked the tabnanny code as broken and needing fixing, and added a test process fork to address the case where the script was started directly from the command line.
27
HERE = os.path.dirname(os.path.realpath(__file__))
28
1102 by Canonical.com Patch Queue Manager
Lucille had some XXXs which should have been NOTEs
29
30
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.
31
    """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
32
10866.2.14 by Maris Fogels
Remove the tabnanny code as pocketlint and text editors already catch this stuff.
33
    Prior to running the tests this script sets up the test database.
10866.2.7 by Maris Fogels
Split test_on_merge.py into functions, marked the tabnanny code as broken and needing fixing, and added a test process fork to address the case where the script was started directly from the command line.
34
35
    Returns 1 on error, otherwise it returns the testrunner's exit code.
36
    """
37
    if setup_test_database() != 0:
38
        return 1
39
40
    return run_test_process()
41
42
43
def setup_test_database():
44
    """Set up a test instance of our postgresql database.
45
46
    Returns 0 for success, 1 for errors.
47
    """
1064.1.3 by James Henstridge
merge from marius
48
    # Sanity check PostgreSQL version. No point in trying to create a test
49
    # database when PostgreSQL is too old.
5821.2.85 by James Henstridge
Add "make check_launchpad_storm_on_merge" target that runs the tests
50
    con = psycopg2.connect('dbname=template1')
1064.1.3 by James Henstridge
merge from marius
51
    cur = con.cursor()
52
    cur.execute('show server_version')
53
    server_version = cur.fetchone()[0]
54
    try:
55
        numeric_server_version = tuple(map(int, server_version.split('.')))
56
    except ValueError:
57
        # Skip this check if the version number is more complicated than
58
        # we expected.
59
        pass
60
    else:
3257.1.1 by Stuart Bishop
PostgreSQL 8.0+ required
61
        if numeric_server_version < (8, 0):
62
            print 'Your PostgreSQL version is too old.  You need 8.x.x'
1064.1.3 by James Henstridge
merge from marius
63
            print 'You have %s' % server_version
64
            return 1
65
1102 by Canonical.com Patch Queue Manager
Lucille had some XXXs which should have been NOTEs
66
    # Drop the template database if it exists - the Makefile does this
67
    # 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
68
    con = psycopg2.connect('dbname=template1')
2083 by Canonical.com Patch Queue Manager
[r=jamesh] testrunner improvements (?)
69
    con.set_isolation_level(0)
1102 by Canonical.com Patch Queue Manager
Lucille had some XXXs which should have been NOTEs
70
    cur = con.cursor()
1064.1.3 by James Henstridge
merge from marius
71
    try:
2083 by Canonical.com Patch Queue Manager
[r=jamesh] testrunner improvements (?)
72
        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
73
    except psycopg2.ProgrammingError, x:
1520 by Canonical.com Patch Queue Manager
Review and fix database security update code
74
        if 'does not exist' not in str(x):
75
            raise
2083 by Canonical.com Patch Queue Manager
[r=jamesh] testrunner improvements (?)
76
    cur.execute("""
77
        select count(*) from pg_stat_activity
78
        where datname in ('launchpad_dev',
79
            'launchpad_ftest_template', 'launchpad_ftest')
80
        """)
81
    existing_connections = cur.fetchone()[0]
82
    if existing_connections > 0:
83
        print 'Cannot rebuild database. There are %d open connections.' % (
84
                existing_connections,
85
                )
86
        return 1
1102 by Canonical.com Patch Queue Manager
Lucille had some XXXs which should have been NOTEs
87
    cur.close()
88
    con.close()
89
90
    # Build the template database. Tests duplicate this.
10866.2.7 by Maris Fogels
Split test_on_merge.py into functions, marked the tabnanny code as broken and needing fixing, and added a test process fork to address the case where the script was started directly from the command line.
91
    schema_dir = os.path.join(HERE, 'database', 'schema')
10303.1.15 by Gary Poster
make test_on_merge.py work, so buildbot can pass
92
    if os.system('cd %s; make test > /dev/null' % (schema_dir)) != 0:
1764 by Canonical.com Patch Queue Manager
Now make check fails if anything goes wrong when loading the sampledata. r=stub
93
        print 'Failed to create database or load sampledata.'
1102 by Canonical.com Patch Queue Manager
Lucille had some XXXs which should have been NOTEs
94
        return 1
95
96
    # Sanity check the database. No point running tests if the
97
    # bedrock is crumbling.
5821.2.85 by James Henstridge
Add "make check_launchpad_storm_on_merge" target that runs the tests
98
    con = psycopg2.connect('dbname=launchpad_ftest_template')
1102 by Canonical.com Patch Queue Manager
Lucille had some XXXs which should have been NOTEs
99
    cur = con.cursor()
100
    cur.execute('show search_path')
101
    search_path = cur.fetchone()[0]
102
    if search_path != '$user,public,ts2':
103
        print 'Search path incorrect.'
104
        print 'Add the following line to /etc/postgresql/postgresql.conf:'
105
        print "    search_path = '$user,public,ts2'"
1064.1.3 by James Henstridge
merge from marius
106
        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
107
        return 1
108
    cur.execute("""
109
        select pg_encoding_to_char(encoding) as encoding from pg_database
110
        where datname='launchpad_ftest_template'
111
        """)
112
    enc = cur.fetchone()[0]
3242.1.5 by Stuart Bishop
Make test_on_merge.py work with PostgreSQL 8.1
113
    if enc not in ('UNICODE', 'UTF8'):
1102 by Canonical.com Patch Queue Manager
Lucille had some XXXs which should have been NOTEs
114
        print 'Database encoding incorrectly set'
115
        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
116
    cur.execute(r"""
117
        SELECT setting FROM pg_settings
118
        WHERE context='internal' AND name='lc_ctype'
119
        """)
120
    loc = cur.fetchone()[0]
2083 by Canonical.com Patch Queue Manager
[r=jamesh] testrunner improvements (?)
121
    #if not (loc.startswith('en_') or loc in ('C', 'en')):
122
    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
123
        print 'Database locale incorrectly set. Need to rerun initdb.'
124
        return 1
125
1102 by Canonical.com Patch Queue Manager
Lucille had some XXXs which should have been NOTEs
126
    # Explicity close our connections - things will fail if we leave open
127
    # connections.
128
    cur.close()
129
    del cur
130
    con.close()
131
    del con
132
10866.2.7 by Maris Fogels
Split test_on_merge.py into functions, marked the tabnanny code as broken and needing fixing, and added a test process fork to address the case where the script was started directly from the command line.
133
    return 0
134
135
136
def run_test_process():
137
    """Start the testrunner process and return its exit code."""
1102 by Canonical.com Patch Queue Manager
Lucille had some XXXs which should have been NOTEs
138
    print 'Running tests.'
10866.2.7 by Maris Fogels
Split test_on_merge.py into functions, marked the tabnanny code as broken and needing fixing, and added a test process fork to address the case where the script was started directly from the command line.
139
    os.chdir(HERE)
10866.2.1 by Maris Fogels
Rewrote the signal handling to move the sentinal into its own process group, where it won't be killed along with its children. Move the process group control code from the testrunner into the sentinal.
140
141
    # We run the test suite under a virtual frame buffer server so that the
142
    # JavaScript integration test suite can run.
10096.2.2 by Bjorn Tillenius
Run xvfb-run inside test_on_merge.py.
143
    cmd = [
10866.2.15 by Maris Fogels
Re-added the shell option to our subprocess call. The script fails with strange errors if we do not include this option.
144
        '/usr/bin/xvfb-run',
11243.1.1 by Maris Fogels
Make Xvfb log its startup errors, and clarify the startup arguments a bit.
145
        "--error-file=/var/tmp/xvfb-errors.log",
146
        "--server-args='-screen 0 1024x768x24'",
10866.2.7 by Maris Fogels
Split test_on_merge.py into functions, marked the tabnanny code as broken and needing fixing, and added a test process fork to address the case where the script was started directly from the command line.
147
        os.path.join(HERE, 'bin', 'test')] + sys.argv[1:]
10096.2.3 by Bjorn Tillenius
Use xvfb-run in test_on_merge.py
148
    command_line = ' '.join(cmd)
10866.2.1 by Maris Fogels
Rewrote the signal handling to move the sentinal into its own process group, where it won't be killed along with its children. Move the process group control code from the testrunner into the sentinal.
149
    print "Running command:", command_line
4107.1.9 by Stuart Bishop
Increase test_on_merge timeout to one hour
150
10866.2.12 by Maris Fogels
Simplified much of the process killing and shenanigans code.
151
    # Run the test suite.  Make the suite the leader of a new process group
152
    # so that we can signal the group without signaling ourselves.
10866.2.1 by Maris Fogels
Rewrote the signal handling to move the sentinal into its own process group, where it won't be killed along with its children. Move the process group control code from the testrunner into the sentinal.
153
    xvfb_proc = Popen(
10866.2.12 by Maris Fogels
Simplified much of the process killing and shenanigans code.
154
        command_line,
155
        stdout=PIPE,
156
        stderr=STDOUT,
10866.2.15 by Maris Fogels
Re-added the shell option to our subprocess call. The script fails with strange errors if we do not include this option.
157
        preexec_fn=os.setpgrp,
158
        shell=True)
10866.2.1 by Maris Fogels
Rewrote the signal handling to move the sentinal into its own process group, where it won't be killed along with its children. Move the process group control code from the testrunner into the sentinal.
159
10866.2.4 by Maris Fogels
Tidied the code for review.
160
    # This code is very similar to what takes place in Popen._communicate(),
10866.2.1 by Maris Fogels
Rewrote the signal handling to move the sentinal into its own process group, where it won't be killed along with its children. Move the process group control code from the testrunner into the sentinal.
161
    # but this code times out if there is no activity on STDOUT for too long.
10866.2.12 by Maris Fogels
Simplified much of the process killing and shenanigans code.
162
    # This keeps us from blocking when reading from a hung testrunner, allows
163
    # us to time out if the child process hangs, and avoids issues when using
164
    # Popen.communicate() with large data sets.
10866.2.1 by Maris Fogels
Rewrote the signal handling to move the sentinal into its own process group, where it won't be killed along with its children. Move the process group control code from the testrunner into the sentinal.
165
    open_readers = set([xvfb_proc.stdout])
3308.1.4 by Stuart Bishop
Fix test_on_merge.py incremental output
166
    while open_readers:
2083 by Canonical.com Patch Queue Manager
[r=jamesh] testrunner improvements (?)
167
        rlist, wlist, xlist = select(open_readers, [], [], TIMEOUT)
168
4092.2.18 by Barry Warsaw
Respond to spiv's review:
169
        if len(rlist) == 0:
10866.2.1 by Maris Fogels
Rewrote the signal handling to move the sentinal into its own process group, where it won't be killed along with its children. Move the process group control code from the testrunner into the sentinal.
170
            # The select() statement timed out!
171
172
            if xvfb_proc.poll() is not None:
173
                # The process we were watching died.
2083 by Canonical.com Patch Queue Manager
[r=jamesh] testrunner improvements (?)
174
                break
10866.2.1 by Maris Fogels
Rewrote the signal handling to move the sentinal into its own process group, where it won't be killed along with its children. Move the process group control code from the testrunner into the sentinal.
175
10866.2.5 by Maris Fogels
Nicer function name
176
            cleanup_hung_testrunner(xvfb_proc)
4092.2.12 by Barry Warsaw
Thanks to jamesh, do a better job of draining the subprocess's stdout and
177
            break
2083 by Canonical.com Patch Queue Manager
[r=jamesh] testrunner improvements (?)
178
10866.2.1 by Maris Fogels
Rewrote the signal handling to move the sentinal into its own process group, where it won't be killed along with its children. Move the process group control code from the testrunner into the sentinal.
179
        if xvfb_proc.stdout in rlist:
180
            # Read a chunk of output from STDOUT.
181
            chunk = os.read(xvfb_proc.stdout.fileno(), 1024)
3367.1.2 by Stuart Bishop
Don't insert random spaces in test_on_merge.py output
182
            sys.stdout.write(chunk)
3308.1.2 by Stuart Bishop
Make test_on_merge.py do incremental output
183
            if chunk == "":
10866.2.1 by Maris Fogels
Rewrote the signal handling to move the sentinal into its own process group, where it won't be killed along with its children. Move the process group control code from the testrunner into the sentinal.
184
                # Gracefully exit the loop if STDOUT is empty.
185
                open_readers.remove(xvfb_proc.stdout)
186
10866.2.4 by Maris Fogels
Tidied the code for review.
187
    rv = xvfb_proc.wait()
10866.2.1 by Maris Fogels
Rewrote the signal handling to move the sentinal into its own process group, where it won't be killed along with its children. Move the process group control code from the testrunner into the sentinal.
188
3308.1.2 by Stuart Bishop
Make test_on_merge.py do incremental output
189
    if rv == 0:
10866.2.1 by Maris Fogels
Rewrote the signal handling to move the sentinal into its own process group, where it won't be killed along with its children. Move the process group control code from the testrunner into the sentinal.
190
        print
191
        print 'Successfully ran all tests.'
1102 by Canonical.com Patch Queue Manager
Lucille had some XXXs which should have been NOTEs
192
    else:
10866.2.1 by Maris Fogels
Rewrote the signal handling to move the sentinal into its own process group, where it won't be killed along with its children. Move the process group control code from the testrunner into the sentinal.
193
        print
194
        print 'Tests failed (exit code %d)' % rv
3308.1.2 by Stuart Bishop
Make test_on_merge.py do incremental output
195
196
    return rv
197
1102 by Canonical.com Patch Queue Manager
Lucille had some XXXs which should have been NOTEs
198
10866.2.5 by Maris Fogels
Nicer function name
199
def cleanup_hung_testrunner(process):
10866.2.4 by Maris Fogels
Tidied the code for review.
200
    """Kill and clean up the testrunner process and its children."""
201
    print
202
    print
203
    print ("WARNING: A test appears to be hung. There has been no "
204
        "output for %d seconds." % TIMEOUT)
205
    print "Forcibly shutting down the test suite"
206
10866.2.12 by Maris Fogels
Simplified much of the process killing and shenanigans code.
207
    # This guarantees the process will die.  In rare cases
10866.2.4 by Maris Fogels
Tidied the code for review.
208
    # a child process may survive this if they are in a different
209
    # process group and they ignore the signals we send their parent.
10866.2.12 by Maris Fogels
Simplified much of the process killing and shenanigans code.
210
    nice_killpg(process.pid)
211
212
    # The process should absolutely be dead now.
213
    assert process.poll() is not None
10866.2.4 by Maris Fogels
Tidied the code for review.
214
215
    # Drain the subprocess's stdout and stderr.
216
    print "The dying processes left behind the following output:"
217
    print "--------------- BEGIN OUTPUT ---------------"
218
    sys.stdout.write(process.stdout.read())
219
    print
220
    print "---------------- END OUTPUT ----------------"
221
222
10866.2.12 by Maris Fogels
Simplified much of the process killing and shenanigans code.
223
def nice_killpg(pgid):
10866.2.1 by Maris Fogels
Rewrote the signal handling to move the sentinal into its own process group, where it won't be killed along with its children. Move the process group control code from the testrunner into the sentinal.
224
    """Kill a Unix process group using increasingly harmful signals."""
2083 by Canonical.com Patch Queue Manager
[r=jamesh] testrunner improvements (?)
225
    try:
10866.2.1 by Maris Fogels
Rewrote the signal handling to move the sentinal into its own process group, where it won't be killed along with its children. Move the process group control code from the testrunner into the sentinal.
226
        print "Process group %d will be killed" % pgid
227
228
        # Attempt a series of increasingly brutal methods of killing the
229
        # process.
230
        for signum in [SIGTERM, SIGINT, SIGHUP, SIGKILL]:
231
            print "Sending signal %s to process group %d" % (signum, pgid)
232
            os.killpg(pgid, signum)
233
234
            # Give the processes some time to shut down.
235
            time.sleep(3)
236
237
    except OSError, exc:
238
        if exc.errno == errno.ESRCH:
239
            # We tried to call os.killpg() and found the group to be empty.
240
            pass
2083 by Canonical.com Patch Queue Manager
[r=jamesh] testrunner improvements (?)
241
        else:
242
            raise
10866.2.1 by Maris Fogels
Rewrote the signal handling to move the sentinal into its own process group, where it won't be killed along with its children. Move the process group control code from the testrunner into the sentinal.
243
    print "Process group %d is now empty." % pgid
244
2083 by Canonical.com Patch Queue Manager
[r=jamesh] testrunner improvements (?)
245
1102 by Canonical.com Patch Queue Manager
Lucille had some XXXs which should have been NOTEs
246
if __name__ == '__main__':
247
    sys.exit(main())