aboutsummaryrefslogtreecommitdiffstats
path: root/build/scripts/run_junit.py
blob: 6944144fa7e7d35859bb7b14aae2cb99ed5909e5 (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
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
import collections 
import json 
import time 
import os 
import sys 
 
SHUTDOWN_SIGNAL = 'SIGUSR1' 
 
PROVIDES = { 
    "devtools/junit-runner/devtools-junit-runner.jar": "junit-runner", 
    "devtools/junit5-runner/devtools-junit5-runner.jar": "junit-runner", 
} 
 
 
class SignalInterruptionError(Exception): 
    pass 
 
 
def on_shutdown(s, f): 
    raise SignalInterruptionError() 
 
 
def get_tracefile_path(args): 
    return args[args.index('--output') + 1] 
 
 
def dump_chunk_error(tracefile, name, imps): 
    with open(tracefile, 'a') as afile: 
        msg = { 
            "timestamp": time.time(), 
            "name": "chunk-event", 
            "value": { 
                "errors": [ 
                    [ 
                        "fail", 
                        "[[bad]]Test contains conflicting dependencies for [[imp]]{}[[bad]]: {}[[rst]]".format( 
                            name, ', '.join(imps) 
                        ), 
                    ], 
                ], 
            }, 
        } 
        json.dump(msg, afile) 
        afile.write("\n") 
 
 
def verify_classpath(args): 
    cpfile = args[args.index('-classpath') + 1] 
    assert cpfile.startswith('@'), cpfile 
 
    cpfile = cpfile[1:] 
    assert os.path.exists(cpfile) 
 
    with open(cpfile) as afile: 
        data = afile.read().splitlines() 
 
    collisions = collections.defaultdict(set) 
    for cp in data: 
        if cp in PROVIDES: 
            collisions[PROVIDES[cp]].add(cp) 
 
    for name, imps in collisions.items(): 
        if len(imps) > 1: 
            tracefile = get_tracefile_path(args) 
            dump_chunk_error(tracefile, name, imps) 
            return False 
    return True 
 
 
def main(): 
    args = sys.argv[1:] 
 
    # Emulates PROVIDES(X) for junit-runner and junit5-runner. 
    # For more info see DEVTOOLSSUPPORT-7454 
    if not verify_classpath(args): 
        return 1 
 
    def execve(): 
        os.execve(args[0], args, os.environ) 
 
    jar_binary = args[args.index('--jar-binary') + 1] 
    java_bin_dir = os.path.dirname(jar_binary) 
    jstack_binary = os.path.join(java_bin_dir, 'jstack') 
 
    if not os.path.exists(jstack_binary): 
        sys.stderr.write("jstack is missing: {}\n".format(jstack_binary)) 
        execve() 
 
    import signal 
 
    signum = getattr(signal, SHUTDOWN_SIGNAL, None) 
 
    if signum is None: 
        execve() 
 
    import subprocess 
 
    proc = subprocess.Popen(args) 
    signal.signal(signum, on_shutdown) 
    timeout = False 
 
    try: 
        proc.wait() 
    except SignalInterruptionError: 
        sys.stderr.write("\nGot {} signal: going to shutdown junit\n".format(signum)) 
        # Dump stack traces 
        subprocess.call([jstack_binary, str(proc.pid)], stdout=sys.stderr) 
        # Kill junit - for more info see DEVTOOLS-7636 
        os.kill(proc.pid, signal.SIGKILL) 
        proc.wait() 
        timeout = True 
 
    if proc.returncode: 
        sys.stderr.write('java exit code: {}\n'.format(proc.returncode)) 
        if timeout: 
            # In case of timeout return specific exit code 
            # https://a.yandex-team.ru/arc/trunk/arcadia/devtools/ya/test/const/__init__.py?rev=r8578188#L301 
            proc.returncode = 10 
            sys.stderr.write('java exit code changed to {}\n'.format(proc.returncode)) 
 
    return proc.returncode 
 
 
if __name__ == '__main__': 
    exit(main())