summaryrefslogtreecommitdiffstats
path: root/cppunit-parallelize.py
blob: 048e9d919b654c64e54f2551e489c810ad65e1c6 (plain) (blame)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
import sys
import argparse
import copy
import os
import subprocess
import time
import collections

def parse_arguments():
    argparser = argparse.ArgumentParser(description="Run Vespa cppunit tests in parallell")
    argparser.add_argument("testrunner", type=str, help="Test runner executable")
    argparser.add_argument("--chunks", type=int, help="Number of chunks", default=5)
    return argparser.parse_args()

def take(lst, n):
    return [ lst.pop() for i in xrange(n) ]

def chunkify(lst, chunks):
    lst = copy.copy(lst)
    chunk_size = len(lst) / chunks
    chunk_surplus = len(lst) % chunks

    result = [ take(lst, chunk_size) for i in xrange(chunks) ]
    if chunk_surplus:
        result.append(lst)

    return result

def build_processes(test_groups):
    processes = []
    for group in test_groups:
        cmd = (args.testrunner,) + tuple(group)
        processes.append((group,
                          subprocess.Popen(
                              cmd,
                              stdout=subprocess.PIPE,
                              stderr=subprocess.STDOUT,
                              preexec_fn=os.setpgrp)))
    return processes

args = parse_arguments()
test_suites = subprocess.check_output((args.testrunner, "--list")).strip().split("\n")
test_suite_groups = chunkify(test_suites, args.chunks)
processes = build_processes(test_suite_groups)
output = collections.defaultdict(str)

print "Running %d test suites in %d parallel chunks with ~%d tests each" % (len(test_suites), len(test_suite_groups), len(test_suite_groups[0]))

while True:
    prevlen = len(processes)
    for group, proc in processes:
        return_code = proc.poll()
        output[proc] += proc.stdout.read()

        if return_code == 0:
            processes.remove((group, proc))
            if not len(processes):
                print "All tests suites ran successfully"
                sys.exit(0)
        elif return_code is not None:
            print "One of '%s' test suites failed:" % ", ".join(group)
            print >>sys.stderr, output[proc]
            sys.exit(return_code)

        if prevlen != len(processes):
            prevlen = len(processes)
            print "%d test suite(s) left" % prevlen

        time.sleep(0.01)