Add link to the blog posting.
[patchq.git] / perform-tests.py
1 #!/usr/bin/python
2 # -*- coding: utf-8 -*-
3
4 # This task takes patch series messages off one of the ‘patchq_test_*’
5 # queues and tests the patch series.
6 #
7 # If you run it without arguments, it will pick up patches from each
8 # test queue in turn and test them (but not in parallel).  You can
9 # also list one or more tests on the command line, in which case only
10 # that test or tests are considered.
11 #
12 # This script does not perform tests in parallel, but doesn't care if
13 # other instances of the script are running (even across machines).
14 # Because of the message broker each patch series is only tested once.
15
16 import email
17 import json
18 import mailbox
19 import os
20 import pika
21 import shutil
22 import subprocess
23 import sys
24 import tempfile
25
26 import config
27
28 connection = pika.BlockingConnection(pika.ConnectionParameters(
29     host = config.mq_server))
30 channel = connection.channel()
31
32 def ack(method):
33     channel.basic_ack(delivery_tag = method.delivery_tag)
34
35 # Which tests to run?
36 if len(sys.argv) <= 1:
37     tests = config.tests
38 else:
39     tests = []
40     for arg in sys.argv[1:]:
41         if arg not in config.tests:
42             sys.exit("%s is not listed in config.tests" % arg)
43         tests.append(arg)
44
45 pwd = os.getcwd()
46
47 for t in tests:
48     qname = "patchq_test_%s" % t
49     while True:
50         method, _, body = channel.basic_get(queue = qname, no_ack = False)
51         if not method: break
52
53         # Parse the ordered list of messages forming the patch series.
54         msgs = json.loads(body)
55         msgs = [email.message_from_string(m) for m in msgs]
56
57         # This should never happen, but the rest of the code
58         # below assumes number of msgs > 0, so ...
59         if len(msgs) == 0:
60             ack(method)
61             continue
62
63         print ("%s: Running test:" % t)
64
65         # Save them to a temporary directory.
66         dir = tempfile.mkdtemp()
67         os.chdir(dir)
68
69         # Save the patches to files.
70         i = 0
71         args = []
72         args.append("%s/%s.sh" % (pwd, t))
73         last_msg = None
74         for m in msgs:
75             i = i+1
76             filename = ("%05d" % i)
77             args.append(filename)
78             print ("%05d %s" % (i, m['Subject']))
79             with open(filename, "w") as file:
80                 file.write(m.as_string())
81             last_msg = m
82
83         # Run the test.
84         with open("output", "w") as out:
85             r = subprocess.call(args, stdout=out, stderr=out)
86
87         if r == 77:
88             print ("%s: Test skipped" % t)
89         else:
90             # Do a "group reply" to the last email.
91             tos = last_msg.get_all('to', [])
92             ccs = last_msg.get_all('cc', [])
93             from_ = last_msg['From']
94             to = email.utils.getaddresses(tos + ccs + [from_])
95             ref = last_msg['Message-Id']
96             if r == 0:
97                 status = "success"
98             else:
99                 status = "FAILED"
100             subject = "%s %s (was: Re: %s)" % (t, status, last_msg['Subject'])
101             with open("output", "r") as file:
102                 content = file.read()
103
104             body = json.dumps((to, subject, ref, content))
105             channel.basic_publish(exchange = 'patchq_reports',
106                                   routing_key = '',
107                                   body = body)
108
109         # Ack the input message since we have processed it.
110         ack(method)
111
112         shutil.rmtree(dir, ignore_errors = True)