Read-only mirrors. Development, issues and pull requests are on GitHub.

summaryrefslogtreecommitdiffhomepage
path: root/tests/TestScripts/testBazelExitGuardFile.py
blob: 376049d21a44ed7b8363abc5fc85acd9fcd8413c (plain)
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
#!/usr/bin/env python3

#              Copyright Catch2 Authors
# Distributed under the Boost Software License, Version 1.0.
#   (See accompanying file LICENSE.txt or copy at
#        https://www.boost.org/LICENSE_1_0.txt)

# SPDX-License-Identifier: BSL-1.0

"""
Checks the premature-exit guard file: the runner creates it when the run
starts and removes it when the run ends. A test that ends its process is a
crash the runner reports, so the guard is removed then too. The guard is
created only the user can read, and a symbolic link in its place is refused.

Arguments: the test binary and a directory for the guard files.
"""

import os
import subprocess
import sys


def guard_path(directory: str) -> str:
    return os.path.join(directory, 'guard_file.' + os.urandom(8).hex())


def run(cmd, env = None, expected = 0):
    print('Running:', cmd)
    result = subprocess.run(cmd,
                            stdout = subprocess.PIPE,
                            stderr = subprocess.PIPE,
                            universal_newlines = True,
                            env = env)
    if result.returncode != expected:
        print('Return code {} instead of {}'.format(result.returncode, expected))
        print('stdout:', result.stdout)
        print('stderr:', result.stderr)
        exit(1)
    return result


bin_path = os.path.abspath(sys.argv[1])
output_dir = os.path.abspath(sys.argv[2])

# Bazel names the guard in the environment.
path = guard_path(output_dir)
env = os.environ.copy()
env['BAZEL_TEST'] = '1'
env['TEST_PREMATURE_EXIT_FILE'] = path
run([bin_path, 'pass'], env)
assert not os.path.exists(path), 'the guard was left behind after a normal run'

# A test that exits the process is a crash; the run still ends normally.
path = guard_path(output_dir)
run([bin_path, '--retries=off', '--premature-exit-guard-file=' + path], expected = 42)
assert not os.path.exists(path), 'the guard was left behind after a crash'

if os.name != 'nt':
    # A stale guard with loose permissions is tightened while the run lasts.
    path = guard_path(output_dir)
    with open(path, 'w'):
        pass
    os.chmod(path, 0o666)
    run([bin_path, 'pass', '--premature-exit-guard-file=' + path])

    target_path = path + '.target'
    symlink_path = path + '.symlink'
    sentinel = 'do not truncate a symlink target\n'
    with open(target_path, 'w') as target:
        target.write(sentinel)
    os.symlink(target_path, symlink_path)
    try:
        result = subprocess.run(
            [bin_path, 'pass', '--premature-exit-guard-file=' + symlink_path],
            stdout = subprocess.PIPE,
            stderr = subprocess.PIPE,
            universal_newlines = True)
        assert result.returncode != 0, 'a symlink was accepted as the guard file'
        with open(target_path, 'r') as target:
            assert target.read() == sentinel, 'the symlink target was modified'
    finally:
        os.remove(symlink_path)
        os.remove(target_path)