@@ -99,16 +99,21 @@ def fun():
99
99
import re
100
100
101
101
executable = sys .executable .split (" " ) # HACK: our sys.executable on Java is a cmdline
102
- re_success = re .compile ("^ (test\S+)[^\r \n ]* \.\.\. ok$" , re .MULTILINE )
102
+ re_success = re .compile ("(test\S+) \(( [^\s]+)\) \.\.\. ok$" , re .MULTILINE )
103
103
kwargs = {"stdout" : subprocess .PIPE , "stderr" : subprocess .PIPE , "text" : True , "check" : False }
104
104
105
105
glob_pattern = os .path .join (os .path .dirname (test .__file__ ), "test_*.py" )
106
106
retag = False
107
+ maxrepeats = 2
107
108
for arg in sys .argv [1 :]:
108
109
if arg == "--retag" :
109
110
retag = True
111
+ elif arg .startswith ("--maxrepeats=" ):
112
+ maxrepeats = int (arg .partition ("=" )[2 ])
113
+ elif arg == "--help" :
114
+ print (sys .argv [0 ] + " [--retag] [--maxrepeats=n] [glob]" )
110
115
else :
111
- glob_pattern = os .path .join (os .path .dirname (test .__file__ ), sys . argv [ 1 ] )
116
+ glob_pattern = os .path .join (os .path .dirname (test .__file__ ), arg )
112
117
113
118
p = subprocess .run (["/usr/bin/which" , "timeout" ], ** kwargs )
114
119
if p .returncode != 0 :
@@ -118,43 +123,81 @@ def fun():
118
123
119
124
testfiles = glob .glob (glob_pattern )
120
125
for idx , testfile in enumerate (testfiles ):
121
- testfile_stem = os .path .splitext (os .path .basename (testfile ))[0 ]
122
- testmod = "test." + testfile_stem
123
- cmd = [timeout , "-s" , "9" , "60" ] + executable + ["-S" , "-m" ]
124
- tagfile = os .path .join (TAGS_DIR , testfile_stem + ".txt" )
125
- test_selectors = working_selectors (tagfile )
126
-
127
- if test_selectors is None and not retag :
128
- # there's no tagfile for this, so it's not working at all (or has
129
- # not been tried)
130
- continue
131
-
132
- print ("[%d/%d] Testing %s" % (idx + 1 , len (testfiles ), testmod ))
133
- cmd += ["unittest" , "-v" ]
134
- for selector in test_selectors :
135
- cmd += ["-k" , selector ]
136
- cmd .append (testfile )
137
-
138
- print (" " .join (cmd ))
139
- p = subprocess .run (cmd , ** kwargs )
140
- print ("*stdout*" )
141
- print (p .stdout )
142
- print ("*stderr*" )
143
- print (p .stderr )
144
-
145
- if p .returncode == 0 and not os .path .exists (tagfile ):
146
- # if we're re-tagging a test without tags, all passed
147
- with open (tagfile , "w" ) as f :
148
- pass
149
- else :
150
- passing_tests = []
151
- for m in re_success .findall (p .stdout ):
152
- passing_tests .append (m )
153
- for m in re_success .findall (p .stderr ):
154
- passing_tests .append (m )
155
- with open (tagfile , "w" ) as f :
156
- for passing_test in passing_tests :
157
- f .write (passing_test )
158
- f .write ("\n " )
159
- if not passing_tests :
126
+ for repeat in range (maxrepeats ):
127
+ # we always do this multiple times, because sometimes the tagging
128
+ # doesn't quite work e.g. we create a tags file that'll still fail
129
+ # when we use it. Thus, when we run this multiple times, we'll just
130
+ # use the tags and if it fails in the last run, we assume something
131
+ # sad is happening and delete the tags file to skip the tests
132
+ # entirely
133
+ testfile_stem = os .path .splitext (os .path .basename (testfile ))[0 ]
134
+ testmod = "test." + testfile_stem
135
+ cmd = [timeout , "-s" , "9" , "60" ] + executable + ["-S" , "-m" ]
136
+ tagfile = os .path .join (TAGS_DIR , testfile_stem + ".txt" )
137
+ test_selectors = working_selectors (tagfile )
138
+
139
+ if test_selectors is None :
140
+ if retag :
141
+ test_selectors = []
142
+ else :
143
+ # there's no tagfile for this, so it's not working at all
144
+ # (or has not been tried).
145
+ continue
146
+
147
+ print ("[%d/%d, Try %d] Testing %s" % (idx + 1 , len (testfiles ), repeat + 1 , testmod ))
148
+ cmd += ["unittest" , "-v" ]
149
+ for selector in test_selectors :
150
+ cmd += ["-k" , selector ]
151
+ cmd .append (testfile )
152
+
153
+ print (" " .join (cmd ))
154
+ p = subprocess .run (cmd , ** kwargs )
155
+ print ("*stdout*" )
156
+ print (p .stdout )
157
+ print ("*stderr*" )
158
+ print (p .stderr )
159
+
160
+ if p .returncode == 0 and not os .path .exists (tagfile ):
161
+ # if we're re-tagging a test without tags, all passed
162
+ with open (tagfile , "w" ) as f :
163
+ pass
164
+ elif p .returncode == 0 :
165
+ # we ran the tagged tests and they were fine
166
+ break
167
+ elif repeat < maxrepeats :
168
+ # we failed the first run, create a tag file with the passing
169
+ # tests (if any)
170
+ passing_tests = []
171
+
172
+ try :
173
+ imported_test_module = __import__ (testmod )
174
+ except :
175
+ imported_test_module = None
176
+
177
+ def get_pass_name (funcname , classname ):
178
+ if imported_test_module :
179
+ classname = "" .join (classname .rpartition (testmod )[1 :])
180
+ clazz = imported_test_module
181
+ path_to_class = classname .split ("." )[1 :]
182
+ for part in path_to_class :
183
+ clazz = getattr (clazz , part )
184
+ return getattr (clazz , funcname ).__qualname__
185
+ else :
186
+ return funcname
187
+
188
+ # n.b.: we add a '*' in the front, so that unittests doesn't add
189
+ # its own asterisks, because now this is already a pattern
190
+ for funcname ,classname in re_success .findall (p .stdout ):
191
+ passing_tests .append ("*" + get_pass_name (funcname , classname ))
192
+ for funcname ,classname in re_success .findall (p .stderr ):
193
+ passing_tests .append ("*" + get_pass_name (funcname , classname ))
194
+
195
+ with open (tagfile , "w" ) as f :
196
+ for passing_test in passing_tests :
197
+ f .write (passing_test )
198
+ f .write ("\n " )
199
+ if not passing_tests :
200
+ os .unlink (tagfile )
201
+ else :
202
+ # we tried a second time and failed, so our tags don't work for some reason
160
203
os .unlink (tagfile )
0 commit comments