##// END OF EJS Templates
tests: avoid test-hup hanging on AIX...
Jim Hague -
r16364:f64b25f1 stable
parent child Browse files
Show More
@@ -1,430 +1,432 b''
1 #!/usr/bin/env python
1 #!/usr/bin/env python
2 #
2 #
3 # check-code - a style and portability checker for Mercurial
3 # check-code - a style and portability checker for Mercurial
4 #
4 #
5 # Copyright 2010 Matt Mackall <mpm@selenic.com>
5 # Copyright 2010 Matt Mackall <mpm@selenic.com>
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 import re, glob, os, sys
10 import re, glob, os, sys
11 import keyword
11 import keyword
12 import optparse
12 import optparse
13
13
14 def repquote(m):
14 def repquote(m):
15 t = re.sub(r"\w", "x", m.group('text'))
15 t = re.sub(r"\w", "x", m.group('text'))
16 t = re.sub(r"[^\s\nx]", "o", t)
16 t = re.sub(r"[^\s\nx]", "o", t)
17 return m.group('quote') + t + m.group('quote')
17 return m.group('quote') + t + m.group('quote')
18
18
19 def reppython(m):
19 def reppython(m):
20 comment = m.group('comment')
20 comment = m.group('comment')
21 if comment:
21 if comment:
22 return "#" * len(comment)
22 return "#" * len(comment)
23 return repquote(m)
23 return repquote(m)
24
24
25 def repcomment(m):
25 def repcomment(m):
26 return m.group(1) + "#" * len(m.group(2))
26 return m.group(1) + "#" * len(m.group(2))
27
27
28 def repccomment(m):
28 def repccomment(m):
29 t = re.sub(r"((?<=\n) )|\S", "x", m.group(2))
29 t = re.sub(r"((?<=\n) )|\S", "x", m.group(2))
30 return m.group(1) + t + "*/"
30 return m.group(1) + t + "*/"
31
31
32 def repcallspaces(m):
32 def repcallspaces(m):
33 t = re.sub(r"\n\s+", "\n", m.group(2))
33 t = re.sub(r"\n\s+", "\n", m.group(2))
34 return m.group(1) + t
34 return m.group(1) + t
35
35
36 def repinclude(m):
36 def repinclude(m):
37 return m.group(1) + "<foo>"
37 return m.group(1) + "<foo>"
38
38
39 def rephere(m):
39 def rephere(m):
40 t = re.sub(r"\S", "x", m.group(2))
40 t = re.sub(r"\S", "x", m.group(2))
41 return m.group(1) + t
41 return m.group(1) + t
42
42
43
43
44 testpats = [
44 testpats = [
45 [
45 [
46 (r'(pushd|popd)', "don't use 'pushd' or 'popd', use 'cd'"),
46 (r'(pushd|popd)', "don't use 'pushd' or 'popd', use 'cd'"),
47 (r'\W\$?\(\([^\)\n]*\)\)', "don't use (()) or $(()), use 'expr'"),
47 (r'\W\$?\(\([^\)\n]*\)\)', "don't use (()) or $(()), use 'expr'"),
48 (r'^function', "don't use 'function', use old style"),
48 (r'^function', "don't use 'function', use old style"),
49 (r'grep.*-q', "don't use 'grep -q', redirect to /dev/null"),
49 (r'grep.*-q', "don't use 'grep -q', redirect to /dev/null"),
50 (r'echo.*\\n', "don't use 'echo \\n', use printf"),
50 (r'echo.*\\n', "don't use 'echo \\n', use printf"),
51 (r'echo -n', "don't use 'echo -n', use printf"),
51 (r'echo -n', "don't use 'echo -n', use printf"),
52 (r'^diff.*-\w*N', "don't use 'diff -N'"),
52 (r'^diff.*-\w*N', "don't use 'diff -N'"),
53 (r'(^| )wc[^|]*$\n(?!.*\(re\))', "filter wc output"),
53 (r'(^| )wc[^|]*$\n(?!.*\(re\))', "filter wc output"),
54 (r'head -c', "don't use 'head -c', use 'dd'"),
54 (r'head -c', "don't use 'head -c', use 'dd'"),
55 (r'sha1sum', "don't use sha1sum, use $TESTDIR/md5sum.py"),
55 (r'sha1sum', "don't use sha1sum, use $TESTDIR/md5sum.py"),
56 (r'ls.*-\w*R', "don't use 'ls -R', use 'find'"),
56 (r'ls.*-\w*R', "don't use 'ls -R', use 'find'"),
57 (r'printf.*\\\d{1,3}', "don't use 'printf \NNN', use Python"),
57 (r'printf.*\\\d{1,3}', "don't use 'printf \NNN', use Python"),
58 (r'printf.*\\x', "don't use printf \\x, use Python"),
58 (r'printf.*\\x', "don't use printf \\x, use Python"),
59 (r'\$\(.*\)', "don't use $(expr), use `expr`"),
59 (r'\$\(.*\)', "don't use $(expr), use `expr`"),
60 (r'rm -rf \*', "don't use naked rm -rf, target a directory"),
60 (r'rm -rf \*', "don't use naked rm -rf, target a directory"),
61 (r'(^|\|\s*)grep (-\w\s+)*[^|]*[(|]\w',
61 (r'(^|\|\s*)grep (-\w\s+)*[^|]*[(|]\w',
62 "use egrep for extended grep syntax"),
62 "use egrep for extended grep syntax"),
63 (r'/bin/', "don't use explicit paths for tools"),
63 (r'/bin/', "don't use explicit paths for tools"),
64 (r'\$PWD', "don't use $PWD, use `pwd`"),
64 (r'\$PWD', "don't use $PWD, use `pwd`"),
65 (r'[^\n]\Z', "no trailing newline"),
65 (r'[^\n]\Z', "no trailing newline"),
66 (r'export.*=', "don't export and assign at once"),
66 (r'export.*=', "don't export and assign at once"),
67 (r'^([^"\'\n]|("[^"\n]*")|(\'[^\'\n]*\'))*\\^', "^ must be quoted"),
67 (r'^([^"\'\n]|("[^"\n]*")|(\'[^\'\n]*\'))*\\^', "^ must be quoted"),
68 (r'^source\b', "don't use 'source', use '.'"),
68 (r'^source\b', "don't use 'source', use '.'"),
69 (r'touch -d', "don't use 'touch -d', use 'touch -t' instead"),
69 (r'touch -d', "don't use 'touch -d', use 'touch -t' instead"),
70 (r'ls +[^|\n-]+ +-', "options to 'ls' must come before filenames"),
70 (r'ls +[^|\n-]+ +-', "options to 'ls' must come before filenames"),
71 (r'[^>\n]>\s*\$HGRCPATH', "don't overwrite $HGRCPATH, append to it"),
71 (r'[^>\n]>\s*\$HGRCPATH', "don't overwrite $HGRCPATH, append to it"),
72 (r'^stop\(\)', "don't use 'stop' as a shell function name"),
72 (r'^stop\(\)', "don't use 'stop' as a shell function name"),
73 (r'(\[|\btest\b).*-e ', "don't use 'test -e', use 'test -f'"),
73 (r'(\[|\btest\b).*-e ', "don't use 'test -e', use 'test -f'"),
74 (r'^alias\b.*=', "don't use alias, use a function"),
74 (r'^alias\b.*=', "don't use alias, use a function"),
75 ],
75 ],
76 # warnings
76 # warnings
77 []
77 []
78 ]
78 ]
79
79
80 testfilters = [
80 testfilters = [
81 (r"( *)(#([^\n]*\S)?)", repcomment),
81 (r"( *)(#([^\n]*\S)?)", repcomment),
82 (r"<<(\S+)((.|\n)*?\n\1)", rephere),
82 (r"<<(\S+)((.|\n)*?\n\1)", rephere),
83 ]
83 ]
84
84
85 uprefix = r"^ \$ "
85 uprefix = r"^ \$ "
86 uprefixc = r"^ > "
86 uprefixc = r"^ > "
87 utestpats = [
87 utestpats = [
88 [
88 [
89 (r'^(\S| $ ).*(\S[ \t]+|^[ \t]+)\n', "trailing whitespace on non-output"),
89 (r'^(\S| $ ).*(\S[ \t]+|^[ \t]+)\n', "trailing whitespace on non-output"),
90 (uprefix + r'.*\|\s*sed', "use regex test output patterns instead of sed"),
90 (uprefix + r'.*\|\s*sed', "use regex test output patterns instead of sed"),
91 (uprefix + r'(true|exit 0)', "explicit zero exit unnecessary"),
91 (uprefix + r'(true|exit 0)', "explicit zero exit unnecessary"),
92 (uprefix + r'.*(?<!\[)\$\?', "explicit exit code checks unnecessary"),
92 (uprefix + r'.*(?<!\[)\$\?', "explicit exit code checks unnecessary"),
93 (uprefix + r'.*\|\| echo.*(fail|error)',
93 (uprefix + r'.*\|\| echo.*(fail|error)',
94 "explicit exit code checks unnecessary"),
94 "explicit exit code checks unnecessary"),
95 (uprefix + r'set -e', "don't use set -e"),
95 (uprefix + r'set -e', "don't use set -e"),
96 (uprefixc + r'( *)\t', "don't use tabs to indent"),
96 (uprefixc + r'( *)\t', "don't use tabs to indent"),
97 (uprefixc + r'.*do\s*true;\s*done',
98 "don't use true as loop body, use sleep 0"),
97 ],
99 ],
98 # warnings
100 # warnings
99 []
101 []
100 ]
102 ]
101
103
102 for i in [0, 1]:
104 for i in [0, 1]:
103 for p, m in testpats[i]:
105 for p, m in testpats[i]:
104 if p.startswith(r'^'):
106 if p.startswith(r'^'):
105 p = uprefix + p[1:]
107 p = uprefix + p[1:]
106 else:
108 else:
107 p = uprefix + ".*" + p
109 p = uprefix + ".*" + p
108 utestpats[i].append((p, m))
110 utestpats[i].append((p, m))
109
111
110 utestfilters = [
112 utestfilters = [
111 (r"( *)(#([^\n]*\S)?)", repcomment),
113 (r"( *)(#([^\n]*\S)?)", repcomment),
112 ]
114 ]
113
115
114 pypats = [
116 pypats = [
115 [
117 [
116 (r'^\s*def\s*\w+\s*\(.*,\s*\(',
118 (r'^\s*def\s*\w+\s*\(.*,\s*\(',
117 "tuple parameter unpacking not available in Python 3+"),
119 "tuple parameter unpacking not available in Python 3+"),
118 (r'lambda\s*\(.*,.*\)',
120 (r'lambda\s*\(.*,.*\)',
119 "tuple parameter unpacking not available in Python 3+"),
121 "tuple parameter unpacking not available in Python 3+"),
120 (r'(?<!def)\s+(cmp)\(', "cmp is not available in Python 3+"),
122 (r'(?<!def)\s+(cmp)\(', "cmp is not available in Python 3+"),
121 (r'\breduce\s*\(.*', "reduce is not available in Python 3+"),
123 (r'\breduce\s*\(.*', "reduce is not available in Python 3+"),
122 (r'\.has_key\b', "dict.has_key is not available in Python 3+"),
124 (r'\.has_key\b', "dict.has_key is not available in Python 3+"),
123 (r'^\s*\t', "don't use tabs"),
125 (r'^\s*\t', "don't use tabs"),
124 (r'\S;\s*\n', "semicolon"),
126 (r'\S;\s*\n', "semicolon"),
125 (r'\w,\w', "missing whitespace after ,"),
127 (r'\w,\w', "missing whitespace after ,"),
126 (r'\w[+/*\-<>]\w', "missing whitespace in expression"),
128 (r'\w[+/*\-<>]\w', "missing whitespace in expression"),
127 (r'^\s+\w+=\w+[^,)\n]$', "missing whitespace in assignment"),
129 (r'^\s+\w+=\w+[^,)\n]$', "missing whitespace in assignment"),
128 (r'(\s+)try:\n((?:\n|\1\s.*\n)+?)\1except.*?:\n'
130 (r'(\s+)try:\n((?:\n|\1\s.*\n)+?)\1except.*?:\n'
129 r'((?:\n|\1\s.*\n)+?)\1finally:', 'no try/except/finally in Py2.4'),
131 r'((?:\n|\1\s.*\n)+?)\1finally:', 'no try/except/finally in Py2.4'),
130 (r'.{85}', "line too long"),
132 (r'.{85}', "line too long"),
131 (r' x+[xo][\'"]\n\s+[\'"]x', 'string join across lines with no space'),
133 (r' x+[xo][\'"]\n\s+[\'"]x', 'string join across lines with no space'),
132 (r'[^\n]\Z', "no trailing newline"),
134 (r'[^\n]\Z', "no trailing newline"),
133 (r'(\S[ \t]+|^[ \t]+)\n', "trailing whitespace"),
135 (r'(\S[ \t]+|^[ \t]+)\n', "trailing whitespace"),
134 # (r'^\s+[^_ \n][^_. \n]+_[^_\n]+\s*=', "don't use underbars in identifiers"),
136 # (r'^\s+[^_ \n][^_. \n]+_[^_\n]+\s*=', "don't use underbars in identifiers"),
135 (r'^\s+(self\.)?[A-za-z][a-z0-9]+[A-Z]\w* = ',
137 (r'^\s+(self\.)?[A-za-z][a-z0-9]+[A-Z]\w* = ',
136 "don't use camelcase in identifiers"),
138 "don't use camelcase in identifiers"),
137 (r'^\s*(if|while|def|class|except|try)\s[^[\n]*:\s*[^\\n]#\s]+',
139 (r'^\s*(if|while|def|class|except|try)\s[^[\n]*:\s*[^\\n]#\s]+',
138 "linebreak after :"),
140 "linebreak after :"),
139 (r'class\s[^( \n]+:', "old-style class, use class foo(object)"),
141 (r'class\s[^( \n]+:', "old-style class, use class foo(object)"),
140 (r'class\s[^( \n]+\(\):',
142 (r'class\s[^( \n]+\(\):',
141 "class foo() not available in Python 2.4, use class foo(object)"),
143 "class foo() not available in Python 2.4, use class foo(object)"),
142 (r'\b(%s)\(' % '|'.join(keyword.kwlist),
144 (r'\b(%s)\(' % '|'.join(keyword.kwlist),
143 "Python keyword is not a function"),
145 "Python keyword is not a function"),
144 (r',]', "unneeded trailing ',' in list"),
146 (r',]', "unneeded trailing ',' in list"),
145 # (r'class\s[A-Z][^\(]*\((?!Exception)',
147 # (r'class\s[A-Z][^\(]*\((?!Exception)',
146 # "don't capitalize non-exception classes"),
148 # "don't capitalize non-exception classes"),
147 # (r'in range\(', "use xrange"),
149 # (r'in range\(', "use xrange"),
148 # (r'^\s*print\s+', "avoid using print in core and extensions"),
150 # (r'^\s*print\s+', "avoid using print in core and extensions"),
149 (r'[\x80-\xff]', "non-ASCII character literal"),
151 (r'[\x80-\xff]', "non-ASCII character literal"),
150 (r'("\')\.format\(', "str.format() not available in Python 2.4"),
152 (r'("\')\.format\(', "str.format() not available in Python 2.4"),
151 (r'^\s*with\s+', "with not available in Python 2.4"),
153 (r'^\s*with\s+', "with not available in Python 2.4"),
152 (r'\.isdisjoint\(', "set.isdisjoint not available in Python 2.4"),
154 (r'\.isdisjoint\(', "set.isdisjoint not available in Python 2.4"),
153 (r'^\s*except.* as .*:', "except as not available in Python 2.4"),
155 (r'^\s*except.* as .*:', "except as not available in Python 2.4"),
154 (r'^\s*os\.path\.relpath', "relpath not available in Python 2.4"),
156 (r'^\s*os\.path\.relpath', "relpath not available in Python 2.4"),
155 (r'(?<!def)\s+(any|all|format)\(',
157 (r'(?<!def)\s+(any|all|format)\(',
156 "any/all/format not available in Python 2.4"),
158 "any/all/format not available in Python 2.4"),
157 (r'(?<!def)\s+(callable)\(',
159 (r'(?<!def)\s+(callable)\(',
158 "callable not available in Python 3, use getattr(f, '__call__', None)"),
160 "callable not available in Python 3, use getattr(f, '__call__', None)"),
159 (r'if\s.*\selse', "if ... else form not available in Python 2.4"),
161 (r'if\s.*\selse', "if ... else form not available in Python 2.4"),
160 (r'^\s*(%s)\s\s' % '|'.join(keyword.kwlist),
162 (r'^\s*(%s)\s\s' % '|'.join(keyword.kwlist),
161 "gratuitous whitespace after Python keyword"),
163 "gratuitous whitespace after Python keyword"),
162 (r'([\(\[][ \t]\S)|(\S[ \t][\)\]])', "gratuitous whitespace in () or []"),
164 (r'([\(\[][ \t]\S)|(\S[ \t][\)\]])', "gratuitous whitespace in () or []"),
163 # (r'\s\s=', "gratuitous whitespace before ="),
165 # (r'\s\s=', "gratuitous whitespace before ="),
164 (r'[^>< ](\+=|-=|!=|<>|<=|>=|<<=|>>=)\S',
166 (r'[^>< ](\+=|-=|!=|<>|<=|>=|<<=|>>=)\S',
165 "missing whitespace around operator"),
167 "missing whitespace around operator"),
166 (r'[^>< ](\+=|-=|!=|<>|<=|>=|<<=|>>=)\s',
168 (r'[^>< ](\+=|-=|!=|<>|<=|>=|<<=|>>=)\s',
167 "missing whitespace around operator"),
169 "missing whitespace around operator"),
168 (r'\s(\+=|-=|!=|<>|<=|>=|<<=|>>=)\S',
170 (r'\s(\+=|-=|!=|<>|<=|>=|<<=|>>=)\S',
169 "missing whitespace around operator"),
171 "missing whitespace around operator"),
170 (r'[^+=*/!<>&| -](\s=|=\s)[^= ]',
172 (r'[^+=*/!<>&| -](\s=|=\s)[^= ]',
171 "wrong whitespace around ="),
173 "wrong whitespace around ="),
172 (r'raise Exception', "don't raise generic exceptions"),
174 (r'raise Exception', "don't raise generic exceptions"),
173 (r' is\s+(not\s+)?["\'0-9-]', "object comparison with literal"),
175 (r' is\s+(not\s+)?["\'0-9-]', "object comparison with literal"),
174 (r' [=!]=\s+(True|False|None)',
176 (r' [=!]=\s+(True|False|None)',
175 "comparison with singleton, use 'is' or 'is not' instead"),
177 "comparison with singleton, use 'is' or 'is not' instead"),
176 (r'^\s*(while|if) [01]:',
178 (r'^\s*(while|if) [01]:',
177 "use True/False for constant Boolean expression"),
179 "use True/False for constant Boolean expression"),
178 (r'(?<!def)\s+hasattr',
180 (r'(?<!def)\s+hasattr',
179 'hasattr(foo, bar) is broken, use util.safehasattr(foo, bar) instead'),
181 'hasattr(foo, bar) is broken, use util.safehasattr(foo, bar) instead'),
180 (r'opener\([^)]*\).read\(',
182 (r'opener\([^)]*\).read\(',
181 "use opener.read() instead"),
183 "use opener.read() instead"),
182 (r'BaseException', 'not in Py2.4, use Exception'),
184 (r'BaseException', 'not in Py2.4, use Exception'),
183 (r'os\.path\.relpath', 'os.path.relpath is not in Py2.5'),
185 (r'os\.path\.relpath', 'os.path.relpath is not in Py2.5'),
184 (r'opener\([^)]*\).write\(',
186 (r'opener\([^)]*\).write\(',
185 "use opener.write() instead"),
187 "use opener.write() instead"),
186 (r'[\s\(](open|file)\([^)]*\)\.read\(',
188 (r'[\s\(](open|file)\([^)]*\)\.read\(',
187 "use util.readfile() instead"),
189 "use util.readfile() instead"),
188 (r'[\s\(](open|file)\([^)]*\)\.write\(',
190 (r'[\s\(](open|file)\([^)]*\)\.write\(',
189 "use util.readfile() instead"),
191 "use util.readfile() instead"),
190 (r'^[\s\(]*(open(er)?|file)\([^)]*\)',
192 (r'^[\s\(]*(open(er)?|file)\([^)]*\)',
191 "always assign an opened file to a variable, and close it afterwards"),
193 "always assign an opened file to a variable, and close it afterwards"),
192 (r'[\s\(](open|file)\([^)]*\)\.',
194 (r'[\s\(](open|file)\([^)]*\)\.',
193 "always assign an opened file to a variable, and close it afterwards"),
195 "always assign an opened file to a variable, and close it afterwards"),
194 (r'(?i)descendent', "the proper spelling is descendAnt"),
196 (r'(?i)descendent', "the proper spelling is descendAnt"),
195 (r'\.debug\(\_', "don't mark debug messages for translation"),
197 (r'\.debug\(\_', "don't mark debug messages for translation"),
196 ],
198 ],
197 # warnings
199 # warnings
198 [
200 [
199 (r'.{81}', "warning: line over 80 characters"),
201 (r'.{81}', "warning: line over 80 characters"),
200 (r'^\s*except:$', "warning: naked except clause"),
202 (r'^\s*except:$', "warning: naked except clause"),
201 (r'ui\.(status|progress|write|note|warn)\([\'\"]x',
203 (r'ui\.(status|progress|write|note|warn)\([\'\"]x',
202 "warning: unwrapped ui message"),
204 "warning: unwrapped ui message"),
203 ]
205 ]
204 ]
206 ]
205
207
206 pyfilters = [
208 pyfilters = [
207 (r"""(?msx)(?P<comment>\#.*?$)|
209 (r"""(?msx)(?P<comment>\#.*?$)|
208 ((?P<quote>('''|\"\"\"|(?<!')'(?!')|(?<!")"(?!")))
210 ((?P<quote>('''|\"\"\"|(?<!')'(?!')|(?<!")"(?!")))
209 (?P<text>(([^\\]|\\.)*?))
211 (?P<text>(([^\\]|\\.)*?))
210 (?P=quote))""", reppython),
212 (?P=quote))""", reppython),
211 ]
213 ]
212
214
213 cpats = [
215 cpats = [
214 [
216 [
215 (r'//', "don't use //-style comments"),
217 (r'//', "don't use //-style comments"),
216 (r'^ ', "don't use spaces to indent"),
218 (r'^ ', "don't use spaces to indent"),
217 (r'\S\t', "don't use tabs except for indent"),
219 (r'\S\t', "don't use tabs except for indent"),
218 (r'(\S[ \t]+|^[ \t]+)\n', "trailing whitespace"),
220 (r'(\S[ \t]+|^[ \t]+)\n', "trailing whitespace"),
219 (r'.{85}', "line too long"),
221 (r'.{85}', "line too long"),
220 (r'(while|if|do|for)\(', "use space after while/if/do/for"),
222 (r'(while|if|do|for)\(', "use space after while/if/do/for"),
221 (r'return\(', "return is not a function"),
223 (r'return\(', "return is not a function"),
222 (r' ;', "no space before ;"),
224 (r' ;', "no space before ;"),
223 (r'\w+\* \w+', "use int *foo, not int* foo"),
225 (r'\w+\* \w+', "use int *foo, not int* foo"),
224 (r'\([^\)]+\) \w+', "use (int)foo, not (int) foo"),
226 (r'\([^\)]+\) \w+', "use (int)foo, not (int) foo"),
225 (r'\S+ (\+\+|--)', "use foo++, not foo ++"),
227 (r'\S+ (\+\+|--)', "use foo++, not foo ++"),
226 (r'\w,\w', "missing whitespace after ,"),
228 (r'\w,\w', "missing whitespace after ,"),
227 (r'^[^#]\w[+/*]\w', "missing whitespace in expression"),
229 (r'^[^#]\w[+/*]\w', "missing whitespace in expression"),
228 (r'^#\s+\w', "use #foo, not # foo"),
230 (r'^#\s+\w', "use #foo, not # foo"),
229 (r'[^\n]\Z', "no trailing newline"),
231 (r'[^\n]\Z', "no trailing newline"),
230 (r'^\s*#import\b', "use only #include in standard C code"),
232 (r'^\s*#import\b', "use only #include in standard C code"),
231 ],
233 ],
232 # warnings
234 # warnings
233 []
235 []
234 ]
236 ]
235
237
236 cfilters = [
238 cfilters = [
237 (r'(/\*)(((\*(?!/))|[^*])*)\*/', repccomment),
239 (r'(/\*)(((\*(?!/))|[^*])*)\*/', repccomment),
238 (r'''(?P<quote>(?<!")")(?P<text>([^"]|\\")+)"(?!")''', repquote),
240 (r'''(?P<quote>(?<!")")(?P<text>([^"]|\\")+)"(?!")''', repquote),
239 (r'''(#\s*include\s+<)([^>]+)>''', repinclude),
241 (r'''(#\s*include\s+<)([^>]+)>''', repinclude),
240 (r'(\()([^)]+\))', repcallspaces),
242 (r'(\()([^)]+\))', repcallspaces),
241 ]
243 ]
242
244
243 inutilpats = [
245 inutilpats = [
244 [
246 [
245 (r'\bui\.', "don't use ui in util"),
247 (r'\bui\.', "don't use ui in util"),
246 ],
248 ],
247 # warnings
249 # warnings
248 []
250 []
249 ]
251 ]
250
252
251 inrevlogpats = [
253 inrevlogpats = [
252 [
254 [
253 (r'\brepo\.', "don't use repo in revlog"),
255 (r'\brepo\.', "don't use repo in revlog"),
254 ],
256 ],
255 # warnings
257 # warnings
256 []
258 []
257 ]
259 ]
258
260
259 checks = [
261 checks = [
260 ('python', r'.*\.(py|cgi)$', pyfilters, pypats),
262 ('python', r'.*\.(py|cgi)$', pyfilters, pypats),
261 ('test script', r'(.*/)?test-[^.~]*$', testfilters, testpats),
263 ('test script', r'(.*/)?test-[^.~]*$', testfilters, testpats),
262 ('c', r'.*\.c$', cfilters, cpats),
264 ('c', r'.*\.c$', cfilters, cpats),
263 ('unified test', r'.*\.t$', utestfilters, utestpats),
265 ('unified test', r'.*\.t$', utestfilters, utestpats),
264 ('layering violation repo in revlog', r'mercurial/revlog\.py', pyfilters,
266 ('layering violation repo in revlog', r'mercurial/revlog\.py', pyfilters,
265 inrevlogpats),
267 inrevlogpats),
266 ('layering violation ui in util', r'mercurial/util\.py', pyfilters,
268 ('layering violation ui in util', r'mercurial/util\.py', pyfilters,
267 inutilpats),
269 inutilpats),
268 ]
270 ]
269
271
270 class norepeatlogger(object):
272 class norepeatlogger(object):
271 def __init__(self):
273 def __init__(self):
272 self._lastseen = None
274 self._lastseen = None
273
275
274 def log(self, fname, lineno, line, msg, blame):
276 def log(self, fname, lineno, line, msg, blame):
275 """print error related a to given line of a given file.
277 """print error related a to given line of a given file.
276
278
277 The faulty line will also be printed but only once in the case
279 The faulty line will also be printed but only once in the case
278 of multiple errors.
280 of multiple errors.
279
281
280 :fname: filename
282 :fname: filename
281 :lineno: line number
283 :lineno: line number
282 :line: actual content of the line
284 :line: actual content of the line
283 :msg: error message
285 :msg: error message
284 """
286 """
285 msgid = fname, lineno, line
287 msgid = fname, lineno, line
286 if msgid != self._lastseen:
288 if msgid != self._lastseen:
287 if blame:
289 if blame:
288 print "%s:%d (%s):" % (fname, lineno, blame)
290 print "%s:%d (%s):" % (fname, lineno, blame)
289 else:
291 else:
290 print "%s:%d:" % (fname, lineno)
292 print "%s:%d:" % (fname, lineno)
291 print " > %s" % line
293 print " > %s" % line
292 self._lastseen = msgid
294 self._lastseen = msgid
293 print " " + msg
295 print " " + msg
294
296
295 _defaultlogger = norepeatlogger()
297 _defaultlogger = norepeatlogger()
296
298
297 def getblame(f):
299 def getblame(f):
298 lines = []
300 lines = []
299 for l in os.popen('hg annotate -un %s' % f):
301 for l in os.popen('hg annotate -un %s' % f):
300 start, line = l.split(':', 1)
302 start, line = l.split(':', 1)
301 user, rev = start.split()
303 user, rev = start.split()
302 lines.append((line[1:-1], user, rev))
304 lines.append((line[1:-1], user, rev))
303 return lines
305 return lines
304
306
305 def checkfile(f, logfunc=_defaultlogger.log, maxerr=None, warnings=False,
307 def checkfile(f, logfunc=_defaultlogger.log, maxerr=None, warnings=False,
306 blame=False, debug=False, lineno=True):
308 blame=False, debug=False, lineno=True):
307 """checks style and portability of a given file
309 """checks style and portability of a given file
308
310
309 :f: filepath
311 :f: filepath
310 :logfunc: function used to report error
312 :logfunc: function used to report error
311 logfunc(filename, linenumber, linecontent, errormessage)
313 logfunc(filename, linenumber, linecontent, errormessage)
312 :maxerr: number of error to display before arborting.
314 :maxerr: number of error to display before arborting.
313 Set to false (default) to report all errors
315 Set to false (default) to report all errors
314
316
315 return True if no error is found, False otherwise.
317 return True if no error is found, False otherwise.
316 """
318 """
317 blamecache = None
319 blamecache = None
318 result = True
320 result = True
319 for name, match, filters, pats in checks:
321 for name, match, filters, pats in checks:
320 if debug:
322 if debug:
321 print name, f
323 print name, f
322 fc = 0
324 fc = 0
323 if not re.match(match, f):
325 if not re.match(match, f):
324 if debug:
326 if debug:
325 print "Skipping %s for %s it doesn't match %s" % (
327 print "Skipping %s for %s it doesn't match %s" % (
326 name, match, f)
328 name, match, f)
327 continue
329 continue
328 fp = open(f)
330 fp = open(f)
329 pre = post = fp.read()
331 pre = post = fp.read()
330 fp.close()
332 fp.close()
331 if "no-" + "check-code" in pre:
333 if "no-" + "check-code" in pre:
332 if debug:
334 if debug:
333 print "Skipping %s for %s it has no- and check-code" % (
335 print "Skipping %s for %s it has no- and check-code" % (
334 name, f)
336 name, f)
335 break
337 break
336 for p, r in filters:
338 for p, r in filters:
337 post = re.sub(p, r, post)
339 post = re.sub(p, r, post)
338 if warnings:
340 if warnings:
339 pats = pats[0] + pats[1]
341 pats = pats[0] + pats[1]
340 else:
342 else:
341 pats = pats[0]
343 pats = pats[0]
342 # print post # uncomment to show filtered version
344 # print post # uncomment to show filtered version
343
345
344 if debug:
346 if debug:
345 print "Checking %s for %s" % (name, f)
347 print "Checking %s for %s" % (name, f)
346
348
347 prelines = None
349 prelines = None
348 errors = []
350 errors = []
349 for p, msg in pats:
351 for p, msg in pats:
350 # fix-up regexes for multiline searches
352 # fix-up regexes for multiline searches
351 po = p
353 po = p
352 # \s doesn't match \n
354 # \s doesn't match \n
353 p = re.sub(r'(?<!\\)\\s', r'[ \\t]', p)
355 p = re.sub(r'(?<!\\)\\s', r'[ \\t]', p)
354 # [^...] doesn't match newline
356 # [^...] doesn't match newline
355 p = re.sub(r'(?<!\\)\[\^', r'[^\\n', p)
357 p = re.sub(r'(?<!\\)\[\^', r'[^\\n', p)
356
358
357 #print po, '=>', p
359 #print po, '=>', p
358
360
359 pos = 0
361 pos = 0
360 n = 0
362 n = 0
361 for m in re.finditer(p, post, re.MULTILINE):
363 for m in re.finditer(p, post, re.MULTILINE):
362 if prelines is None:
364 if prelines is None:
363 prelines = pre.splitlines()
365 prelines = pre.splitlines()
364 postlines = post.splitlines(True)
366 postlines = post.splitlines(True)
365
367
366 start = m.start()
368 start = m.start()
367 while n < len(postlines):
369 while n < len(postlines):
368 step = len(postlines[n])
370 step = len(postlines[n])
369 if pos + step > start:
371 if pos + step > start:
370 break
372 break
371 pos += step
373 pos += step
372 n += 1
374 n += 1
373 l = prelines[n]
375 l = prelines[n]
374
376
375 if "check-code" + "-ignore" in l:
377 if "check-code" + "-ignore" in l:
376 if debug:
378 if debug:
377 print "Skipping %s for %s:%s (check-code -ignore)" % (
379 print "Skipping %s for %s:%s (check-code -ignore)" % (
378 name, f, n)
380 name, f, n)
379 continue
381 continue
380 bd = ""
382 bd = ""
381 if blame:
383 if blame:
382 bd = 'working directory'
384 bd = 'working directory'
383 if not blamecache:
385 if not blamecache:
384 blamecache = getblame(f)
386 blamecache = getblame(f)
385 if n < len(blamecache):
387 if n < len(blamecache):
386 bl, bu, br = blamecache[n]
388 bl, bu, br = blamecache[n]
387 if bl == l:
389 if bl == l:
388 bd = '%s@%s' % (bu, br)
390 bd = '%s@%s' % (bu, br)
389 errors.append((f, lineno and n + 1, l, msg, bd))
391 errors.append((f, lineno and n + 1, l, msg, bd))
390 result = False
392 result = False
391
393
392 errors.sort()
394 errors.sort()
393 for e in errors:
395 for e in errors:
394 logfunc(*e)
396 logfunc(*e)
395 fc += 1
397 fc += 1
396 if maxerr and fc >= maxerr:
398 if maxerr and fc >= maxerr:
397 print " (too many errors, giving up)"
399 print " (too many errors, giving up)"
398 break
400 break
399
401
400 return result
402 return result
401
403
402 if __name__ == "__main__":
404 if __name__ == "__main__":
403 parser = optparse.OptionParser("%prog [options] [files]")
405 parser = optparse.OptionParser("%prog [options] [files]")
404 parser.add_option("-w", "--warnings", action="store_true",
406 parser.add_option("-w", "--warnings", action="store_true",
405 help="include warning-level checks")
407 help="include warning-level checks")
406 parser.add_option("-p", "--per-file", type="int",
408 parser.add_option("-p", "--per-file", type="int",
407 help="max warnings per file")
409 help="max warnings per file")
408 parser.add_option("-b", "--blame", action="store_true",
410 parser.add_option("-b", "--blame", action="store_true",
409 help="use annotate to generate blame info")
411 help="use annotate to generate blame info")
410 parser.add_option("", "--debug", action="store_true",
412 parser.add_option("", "--debug", action="store_true",
411 help="show debug information")
413 help="show debug information")
412 parser.add_option("", "--nolineno", action="store_false",
414 parser.add_option("", "--nolineno", action="store_false",
413 dest='lineno', help="don't show line numbers")
415 dest='lineno', help="don't show line numbers")
414
416
415 parser.set_defaults(per_file=15, warnings=False, blame=False, debug=False,
417 parser.set_defaults(per_file=15, warnings=False, blame=False, debug=False,
416 lineno=True)
418 lineno=True)
417 (options, args) = parser.parse_args()
419 (options, args) = parser.parse_args()
418
420
419 if len(args) == 0:
421 if len(args) == 0:
420 check = glob.glob("*")
422 check = glob.glob("*")
421 else:
423 else:
422 check = args
424 check = args
423
425
424 ret = 0
426 ret = 0
425 for f in check:
427 for f in check:
426 if not checkfile(f, maxerr=options.per_file, warnings=options.warnings,
428 if not checkfile(f, maxerr=options.per_file, warnings=options.warnings,
427 blame=options.blame, debug=options.debug,
429 blame=options.blame, debug=options.debug,
428 lineno=options.lineno):
430 lineno=options.lineno):
429 ret = 1
431 ret = 1
430 sys.exit(ret)
432 sys.exit(ret)
@@ -1,28 +1,28 b''
1 Test hangup signal in the middle of transaction
1 Test hangup signal in the middle of transaction
2
2
3 $ "$TESTDIR/hghave" serve fifo || exit 80
3 $ "$TESTDIR/hghave" serve fifo || exit 80
4 $ hg init
4 $ hg init
5 $ mkfifo p
5 $ mkfifo p
6 $ hg serve --stdio < p 1>out 2>&1 &
6 $ hg serve --stdio < p 1>out 2>&1 &
7 $ P=$!
7 $ P=$!
8
8
9 Do test while holding fifo open
9 Do test while holding fifo open
10
10
11 $ (
11 $ (
12 > echo lock
12 > echo lock
13 > echo addchangegroup
13 > echo addchangegroup
14 > while [ ! -s .hg/store/journal ]; do true; done
14 > while [ ! -s .hg/store/journal ]; do sleep 0; done
15 > kill -HUP $P
15 > kill -HUP $P
16 > ) > p
16 > ) > p
17
17
18 $ wait
18 $ wait
19 $ cat out
19 $ cat out
20 0
20 0
21 0
21 0
22 adding changesets
22 adding changesets
23 transaction abort!
23 transaction abort!
24 rollback completed
24 rollback completed
25 killed!
25 killed!
26
26
27 $ echo .hg/* .hg/store/*
27 $ echo .hg/* .hg/store/*
28 .hg/00changelog.i .hg/journal.bookmarks .hg/journal.branch .hg/journal.desc .hg/journal.dirstate .hg/requires .hg/store .hg/store/00changelog.i .hg/store/00changelog.i.a .hg/store/journal.phaseroots
28 .hg/00changelog.i .hg/journal.bookmarks .hg/journal.branch .hg/journal.desc .hg/journal.dirstate .hg/requires .hg/store .hg/store/00changelog.i .hg/store/00changelog.i.a .hg/store/journal.phaseroots
@@ -1,82 +1,82 b''
1 $ "$TESTDIR/hghave" serve || exit 80
1 $ "$TESTDIR/hghave" serve || exit 80
2
2
3 $ hgserve()
3 $ hgserve()
4 > {
4 > {
5 > hg serve -a localhost -d --pid-file=hg.pid -E errors.log -v $@ \
5 > hg serve -a localhost -d --pid-file=hg.pid -E errors.log -v $@ \
6 > | sed -e "s/:$HGPORT1\\([^0-9]\\)/:HGPORT1\1/g" \
6 > | sed -e "s/:$HGPORT1\\([^0-9]\\)/:HGPORT1\1/g" \
7 > -e "s/:$HGPORT2\\([^0-9]\\)/:HGPORT2\1/g" \
7 > -e "s/:$HGPORT2\\([^0-9]\\)/:HGPORT2\1/g" \
8 > -e 's/http:\/\/[^/]*\//http:\/\/localhost\//'
8 > -e 's/http:\/\/[^/]*\//http:\/\/localhost\//'
9 > cat hg.pid >> "$DAEMON_PIDS"
9 > cat hg.pid >> "$DAEMON_PIDS"
10 > echo % errors
10 > echo % errors
11 > cat errors.log
11 > cat errors.log
12 > if [ "$KILLQUIETLY" = "Y" ]; then
12 > if [ "$KILLQUIETLY" = "Y" ]; then
13 > kill `cat hg.pid` 2>/dev/null
13 > kill `cat hg.pid` 2>/dev/null
14 > else
14 > else
15 > kill `cat hg.pid`
15 > kill `cat hg.pid`
16 > fi
16 > fi
17 > while kill -0 `cat hg.pid` 2>/dev/null; do true; done
17 > while kill -0 `cat hg.pid` 2>/dev/null; do sleep 0; done
18 > }
18 > }
19
19
20 $ hg init test
20 $ hg init test
21 $ cd test
21 $ cd test
22 $ echo '[web]' > .hg/hgrc
22 $ echo '[web]' > .hg/hgrc
23 $ echo 'accesslog = access.log' >> .hg/hgrc
23 $ echo 'accesslog = access.log' >> .hg/hgrc
24 $ echo "port = $HGPORT1" >> .hg/hgrc
24 $ echo "port = $HGPORT1" >> .hg/hgrc
25
25
26 Without -v
26 Without -v
27
27
28 $ hg serve -a localhost -p $HGPORT -d --pid-file=hg.pid -E errors.log
28 $ hg serve -a localhost -p $HGPORT -d --pid-file=hg.pid -E errors.log
29 $ cat hg.pid >> "$DAEMON_PIDS"
29 $ cat hg.pid >> "$DAEMON_PIDS"
30 $ if [ -f access.log ]; then
30 $ if [ -f access.log ]; then
31 $ echo 'access log created - .hg/hgrc respected'
31 $ echo 'access log created - .hg/hgrc respected'
32 access log created - .hg/hgrc respected
32 access log created - .hg/hgrc respected
33 $ fi
33 $ fi
34
34
35 errors
35 errors
36
36
37 $ cat errors.log
37 $ cat errors.log
38
38
39 With -v
39 With -v
40
40
41 $ hgserve
41 $ hgserve
42 listening at http://localhost/ (bound to 127.0.0.1:HGPORT1)
42 listening at http://localhost/ (bound to 127.0.0.1:HGPORT1)
43 % errors
43 % errors
44
44
45 With -v and -p HGPORT2
45 With -v and -p HGPORT2
46
46
47 $ hgserve -p "$HGPORT2"
47 $ hgserve -p "$HGPORT2"
48 listening at http://localhost/ (bound to 127.0.0.1:HGPORT2)
48 listening at http://localhost/ (bound to 127.0.0.1:HGPORT2)
49 % errors
49 % errors
50
50
51 With -v and -p daytime (should fail because low port)
51 With -v and -p daytime (should fail because low port)
52
52
53 $ KILLQUIETLY=Y
53 $ KILLQUIETLY=Y
54 $ hgserve -p daytime
54 $ hgserve -p daytime
55 abort: cannot start server at 'localhost:13': Permission denied
55 abort: cannot start server at 'localhost:13': Permission denied
56 abort: child process failed to start
56 abort: child process failed to start
57 % errors
57 % errors
58 $ KILLQUIETLY=N
58 $ KILLQUIETLY=N
59
59
60 With --prefix foo
60 With --prefix foo
61
61
62 $ hgserve --prefix foo
62 $ hgserve --prefix foo
63 listening at http://localhost/foo/ (bound to 127.0.0.1:HGPORT1)
63 listening at http://localhost/foo/ (bound to 127.0.0.1:HGPORT1)
64 % errors
64 % errors
65
65
66 With --prefix /foo
66 With --prefix /foo
67
67
68 $ hgserve --prefix /foo
68 $ hgserve --prefix /foo
69 listening at http://localhost/foo/ (bound to 127.0.0.1:HGPORT1)
69 listening at http://localhost/foo/ (bound to 127.0.0.1:HGPORT1)
70 % errors
70 % errors
71
71
72 With --prefix foo/
72 With --prefix foo/
73
73
74 $ hgserve --prefix foo/
74 $ hgserve --prefix foo/
75 listening at http://localhost/foo/ (bound to 127.0.0.1:HGPORT1)
75 listening at http://localhost/foo/ (bound to 127.0.0.1:HGPORT1)
76 % errors
76 % errors
77
77
78 With --prefix /foo/
78 With --prefix /foo/
79
79
80 $ hgserve --prefix /foo/
80 $ hgserve --prefix /foo/
81 listening at http://localhost/foo/ (bound to 127.0.0.1:HGPORT1)
81 listening at http://localhost/foo/ (bound to 127.0.0.1:HGPORT1)
82 % errors
82 % errors
General Comments 0
You need to be logged in to leave comments. Login now