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