Show More
@@ -1,133 +1,139 | |||
|
1 | 1 | #!/usr/bin/env python |
|
2 | 2 | |
|
3 | 3 | # fsmonitor-run-tests.py - Run Mercurial tests with fsmonitor enabled |
|
4 | 4 | # |
|
5 | 5 | # Copyright 2017 Facebook, Inc. |
|
6 | 6 | # |
|
7 | 7 | # This software may be used and distributed according to the terms of the |
|
8 | 8 | # GNU General Public License version 2 or any later version. |
|
9 | 9 | # |
|
10 | 10 | # This is a wrapper around run-tests.py that spins up an isolated instance of |
|
11 | 11 | # Watchman and runs the Mercurial tests against it. This ensures that the global |
|
12 | 12 | # version of Watchman isn't affected by anything this test does. |
|
13 | 13 | |
|
14 | 14 | from __future__ import absolute_import |
|
15 | 15 | from __future__ import print_function |
|
16 | 16 | |
|
17 | 17 | import argparse |
|
18 | 18 | import contextlib |
|
19 | 19 | import json |
|
20 | 20 | import os |
|
21 | 21 | import shutil |
|
22 | 22 | import subprocess |
|
23 | 23 | import sys |
|
24 | 24 | import tempfile |
|
25 | 25 | import uuid |
|
26 | 26 | |
|
27 | 27 | osenvironb = getattr(os, 'environb', os.environ) |
|
28 | 28 | |
|
29 | 29 | if sys.version_info > (3, 5, 0): |
|
30 | 30 | PYTHON3 = True |
|
31 | 31 | xrange = range # we use xrange in one place, and we'd rather not use range |
|
32 | 32 | def _bytespath(p): |
|
33 | 33 | return p.encode('utf-8') |
|
34 | 34 | |
|
35 | 35 | elif sys.version_info >= (3, 0, 0): |
|
36 | 36 | print('%s is only supported on Python 3.5+ and 2.7, not %s' % |
|
37 | 37 | (sys.argv[0], '.'.join(str(v) for v in sys.version_info[:3]))) |
|
38 | 38 | sys.exit(70) # EX_SOFTWARE from `man 3 sysexit` |
|
39 | 39 | else: |
|
40 | 40 | PYTHON3 = False |
|
41 | 41 | |
|
42 | 42 | # In python 2.x, path operations are generally done using |
|
43 | 43 | # bytestrings by default, so we don't have to do any extra |
|
44 | 44 | # fiddling there. We define the wrapper functions anyway just to |
|
45 | 45 | # help keep code consistent between platforms. |
|
46 | 46 | def _bytespath(p): |
|
47 | 47 | return p |
|
48 | 48 | |
|
49 | 49 | def getparser(): |
|
50 | 50 | """Obtain the argument parser used by the CLI.""" |
|
51 | 51 | parser = argparse.ArgumentParser( |
|
52 | 52 | description='Run tests with fsmonitor enabled.', |
|
53 | 53 | epilog='Unrecognized options are passed to run-tests.py.') |
|
54 | 54 | # - keep these sorted |
|
55 | 55 | # - none of these options should conflict with any in run-tests.py |
|
56 | 56 | parser.add_argument('--keep-fsmonitor-tmpdir', action='store_true', |
|
57 | 57 | help='keep temporary directory with fsmonitor state') |
|
58 | 58 | parser.add_argument('--watchman', |
|
59 | 59 | help='location of watchman binary (default: watchman in PATH)', |
|
60 | 60 | default='watchman') |
|
61 | 61 | |
|
62 | 62 | return parser |
|
63 | 63 | |
|
64 | 64 | @contextlib.contextmanager |
|
65 | 65 | def watchman(args): |
|
66 | 66 | basedir = tempfile.mkdtemp(prefix='hg-fsmonitor') |
|
67 | 67 | try: |
|
68 | 68 | # Much of this configuration is borrowed from Watchman's test harness. |
|
69 | 69 | cfgfile = os.path.join(basedir, 'config.json') |
|
70 | 70 | # TODO: allow setting a config |
|
71 | 71 | with open(cfgfile, 'w') as f: |
|
72 | 72 | f.write(json.dumps({})) |
|
73 | 73 | |
|
74 | 74 | logfile = os.path.join(basedir, 'log') |
|
75 | 75 | clilogfile = os.path.join(basedir, 'cli-log') |
|
76 | 76 | if os.name == 'nt': |
|
77 | 77 | sockfile = '\\\\.\\pipe\\watchman-test-%s' % uuid.uuid4().hex |
|
78 | 78 | else: |
|
79 | 79 | sockfile = os.path.join(basedir, 'sock') |
|
80 | 80 | pidfile = os.path.join(basedir, 'pid') |
|
81 | 81 | statefile = os.path.join(basedir, 'state') |
|
82 | 82 | |
|
83 | 83 | argv = [ |
|
84 | 84 | args.watchman, |
|
85 | 85 | '--sockname', sockfile, |
|
86 | 86 | '--logfile', logfile, |
|
87 | 87 | '--pidfile', pidfile, |
|
88 | 88 | '--statefile', statefile, |
|
89 | 89 | '--foreground', |
|
90 | 90 | '--log-level=2', # debug logging for watchman |
|
91 | 91 | ] |
|
92 | 92 | |
|
93 | 93 | envb = osenvironb.copy() |
|
94 | 94 | envb[b'WATCHMAN_CONFIG_FILE'] = _bytespath(cfgfile) |
|
95 | 95 | with open(clilogfile, 'wb') as f: |
|
96 | 96 | proc = subprocess.Popen( |
|
97 | 97 | argv, env=envb, stdin=None, stdout=f, stderr=f) |
|
98 | 98 | try: |
|
99 | 99 | yield sockfile |
|
100 | 100 | finally: |
|
101 | 101 | proc.terminate() |
|
102 | 102 | proc.kill() |
|
103 | 103 | finally: |
|
104 | 104 | if args.keep_fsmonitor_tmpdir: |
|
105 | 105 | print('fsmonitor dir available at %s' % basedir) |
|
106 | 106 | else: |
|
107 | 107 | shutil.rmtree(basedir, ignore_errors=True) |
|
108 | 108 | |
|
109 | 109 | def run(): |
|
110 | 110 | parser = getparser() |
|
111 | 111 | args, runtestsargv = parser.parse_known_args() |
|
112 | 112 | |
|
113 | 113 | with watchman(args) as sockfile: |
|
114 | 114 | osenvironb[b'WATCHMAN_SOCK'] = _bytespath(sockfile) |
|
115 | 115 | # Indicate to hghave that we're running with fsmonitor enabled. |
|
116 | 116 | osenvironb[b'HGFSMONITOR_TESTS'] = b'1' |
|
117 | 117 | |
|
118 | 118 | runtestdir = os.path.dirname(__file__) |
|
119 | 119 | runtests = os.path.join(runtestdir, 'run-tests.py') |
|
120 | 120 | blacklist = os.path.join(runtestdir, 'blacklists', 'fsmonitor') |
|
121 | 121 | |
|
122 | 122 | runtestsargv.insert(0, runtests) |
|
123 | 123 | runtestsargv.extend([ |
|
124 | 124 | '--extra-config', |
|
125 | 125 | 'extensions.fsmonitor=', |
|
126 | # specify fsmonitor.mode=paranoid always in order to force | |
|
127 | # fsmonitor extension execute "paranoid" code path | |
|
128 | # | |
|
129 | # TODO: make fsmonitor-run-tests.py accept specific options | |
|
130 | '--extra-config', | |
|
131 | 'fsmonitor.mode=paranoid', | |
|
126 | 132 | '--blacklist', |
|
127 | 133 | blacklist, |
|
128 | 134 | ]) |
|
129 | 135 | |
|
130 | 136 | return subprocess.call(runtestsargv) |
|
131 | 137 | |
|
132 | 138 | if __name__ == '__main__': |
|
133 | 139 | sys.exit(run()) |
General Comments 0
You need to be logged in to leave comments.
Login now