Skip to content

Commit f8a8135

Browse files
committed
Add ability to check results file checksum on fail
Added ability to check results file checksum on tests fail and compare with the checksums of the known issues mentioned in the fragile list. Fragile list should consist of the results files checksums with its issues in the format: fragile = { "retries": 10, "tests": { "bitset.test.lua": { "issues": [ "gh-4095" ], "checksums": [ "050af3a99561a724013995668a4bc71c", "f34be60193cfe9221d3fe50df657e9d3" ] } }} Closes #189
1 parent 58e5214 commit f8a8135

File tree

4 files changed

+41
-17
lines changed

4 files changed

+41
-17
lines changed

lib/test.py

Lines changed: 8 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@
88
import sys
99
import traceback
1010
from functools import partial
11+
from hashlib import md5
1112

1213
try:
1314
from cStringIO import StringIO
@@ -152,9 +153,9 @@ def run(self, server):
152153
it to stdout.
153154
154155
Returns short status of the test as a string: 'skip', 'pass',
155-
'new', 'updated' or 'fail'. There is also one possible value for
156-
short_status, 'disabled', but it returned in the caller,
157-
TestSuite.run_test().
156+
'new', 'updated' or 'fail' and results file checksum on fail.
157+
There is also one possible value for short_status, 'disabled',
158+
but it returned in the caller, TestSuite.run_test().
158159
"""
159160

160161
# Note: test was created before certain worker become known, so we need
@@ -219,6 +220,7 @@ def run(self, server):
219220
self.is_valgrind_clean = not bool(non_empty_logs)
220221

221222
short_status = None
223+
result_checksum = None
222224

223225
if self.skip:
224226
short_status = 'skip'
@@ -252,6 +254,8 @@ def run(self, server):
252254
has_result = os.path.exists(self.tmp_result)
253255
if has_result:
254256
shutil.copy(self.tmp_result, self.reject)
257+
with open(self.tmp_result, mode='rb') as result_file:
258+
result_checksum = md5(result_file.read()).hexdigest()
255259
short_status = 'fail'
256260
color_stdout("[ fail ]\n", schema='test_fail')
257261

@@ -277,7 +281,7 @@ def run(self, server):
277281
"Test failed! Output from log file "
278282
"{0}:\n".format(log_file))
279283
where = ": there were warnings in the valgrind log file(s)"
280-
return short_status
284+
return short_status, result_checksum
281285

282286
def print_diagnostics(self, log_file, message):
283287
"""Print whole lines of client program output leading to test

lib/test_suite.py

Lines changed: 10 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -185,6 +185,12 @@ def fragile_tests(self):
185185
res.append(test)
186186
return res
187187

188+
def get_test_fragile_checksums(self, test):
189+
try:
190+
return self.fragile['tests'][test]['checksums']
191+
except Exception:
192+
return []
193+
188194
def gen_server(self):
189195
try:
190196
return Server(self.ini, test_suite=self)
@@ -237,7 +243,7 @@ def stop_server(self, server, inspector, silent=False, cleanup=True):
237243

238244
def run_test(self, test, server, inspector):
239245
""" Returns short status of the test as a string: 'skip', 'pass',
240-
'new', 'fail', or 'disabled'.
246+
'new', 'fail', or 'disabled' and results file checksum on fail.
241247
"""
242248
test.inspector = inspector
243249
test_name = os.path.basename(test.name)
@@ -251,16 +257,17 @@ def run_test(self, test, server, inspector):
251257
color_stdout(conf.ljust(16), schema='test_var')
252258

253259
if self.is_test_enabled(test, conf, server):
254-
short_status = test.run(server)
260+
short_status, result_checksum = test.run(server)
255261
else:
256262
color_stdout("[ disabled ]\n", schema='t_name')
257263
short_status = 'disabled'
264+
result_checksum = None
258265

259266
# cleanup only if test passed or if --force mode enabled
260267
if lib.Options().args.is_force or short_status == 'pass':
261268
inspector.cleanup_nondefault()
262269

263-
return short_status
270+
return short_status, result_checksum
264271

265272
def is_parallel(self):
266273
return self.ini['is_parallel']

lib/worker.py

Lines changed: 20 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -142,13 +142,15 @@ class WorkerTaskResult(BaseWorkerMessage):
142142
""" Passed into the result queue when a task processed (done) by the
143143
worker. The short_status (string) field intended to give short note whether
144144
the task processed successfully or not, but with little more flexibility
145-
than binary True/False. The task_id (any hashable object) field hold ID of
145+
than binary True/False. The result_checksum (string) field saves the results
146+
file checksum on test fail. The task_id (any hashable object) field hold ID of
146147
the processed task. The show_reproduce_content configuration form suite.ini
147148
"""
148149
def __init__(self, worker_id, worker_name, task_id,
149-
short_status, show_reproduce_content):
150+
short_status, result_checksum, show_reproduce_content):
150151
super(WorkerTaskResult, self).__init__(worker_id, worker_name)
151152
self.short_status = short_status
153+
self.result_checksum = result_checksum
152154
self.task_id = task_id
153155
self.show_reproduce_content = show_reproduce_content
154156

@@ -214,8 +216,9 @@ def current_task(self, task_id):
214216
return WorkerCurrentTask(self.id, self.name, task_name, task_param,
215217
task_result, task_tmp_result)
216218

217-
def wrap_result(self, task_id, short_status):
219+
def wrap_result(self, task_id, short_status, result_checksum):
218220
return WorkerTaskResult(self.id, self.name, task_id, short_status,
221+
result_checksum,
219222
self.suite.show_reproduce_content())
220223

221224
def sigterm_handler(self, signum, frame):
@@ -302,7 +305,7 @@ def run_task(self, task_id):
302305
with open(self.reproduce_file, 'a') as f:
303306
task_id_str = yaml.safe_dump(task.id, default_flow_style=True)
304307
f.write('- ' + task_id_str)
305-
short_status = self.suite.run_test(
308+
short_status, result_checksum = self.suite.run_test(
306309
task, self.server, self.inspector)
307310
except KeyboardInterrupt:
308311
self.report_keyboard_interrupt()
@@ -312,7 +315,7 @@ def run_task(self, task_id):
312315
'\nWorker "%s" received the following error; stopping...\n'
313316
% self.name + traceback.format_exc() + '\n', schema='error')
314317
raise
315-
return short_status
318+
return short_status, result_checksum
316319

317320
def run_loop(self, task_queue, result_queue):
318321
""" called from 'run_all' """
@@ -327,21 +330,29 @@ def run_loop(self, task_queue, result_queue):
327330
break
328331

329332
short_status = None
333+
result_checksum = None
330334
result_queue.put(self.current_task(task_id))
335+
testname = os.path.basename(task_id[0])
336+
fragile_checksums = self.suite.get_test_fragile_checksums(testname)
331337
retries_left = self.suite.fragile_retries()
332338
# let's run till short_status became 'pass'
333339
while short_status != 'pass' and retries_left >= 0:
334340
# print message only after some fails occurred
335341
if short_status == 'fail':
336342
color_stdout(
337343
'Test "%s", conf: "%s"\n'
338-
'\tfrom "fragile" list failed, rerunning ...\n'
339-
% (task_id[0], task_id[1]), schema='error')
344+
'\tfrom "fragile" list failed with results'
345+
' file checksum: "%s", rerunning ...\n'
346+
% (task_id[0], task_id[1], result_checksum), schema='error')
340347
# run task and save the result to short_status
341-
short_status = self.run_task(task_id)
348+
short_status, result_checksum = self.run_task(task_id)
349+
# check if the results file checksum set on fail and if
350+
# the newly created results file is known by checksum
351+
if not result_checksum or (result_checksum not in fragile_checksums):
352+
break
342353
retries_left = retries_left - 1
343354

344-
result_queue.put(self.wrap_result(task_id, short_status))
355+
result_queue.put(self.wrap_result(task_id, short_status, result_checksum))
345356
if not lib.Options().args.is_force and short_status == 'fail':
346357
color_stdout(
347358
'Worker "%s" got failed test; stopping the server...\n'

listeners.py

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -45,6 +45,7 @@ def process_result(self, obj):
4545
if obj.short_status == 'fail':
4646
self.failed_tasks.append((obj.task_id,
4747
obj.worker_name,
48+
obj.result_checksum,
4849
obj.show_reproduce_content))
4950

5051
def print_statistics(self):
@@ -58,10 +59,11 @@ def print_statistics(self):
5859
return False
5960

6061
color_stdout('Failed tasks:\n', schema='test_var')
61-
for task_id, worker_name, show_reproduce_content in self.failed_tasks:
62+
for task_id, worker_name, result_checksum, show_reproduce_content in self.failed_tasks:
6263
logfile = self.get_logfile(worker_name)
6364
task_id_str = yaml.safe_dump(task_id, default_flow_style=True)
6465
color_stdout('- %s' % task_id_str, schema='test_var')
66+
color_stdout('# results file checksum: %s\n' % result_checksum)
6567
color_stdout('# logfile: %s\n' % logfile)
6668
reproduce_file_path = get_reproduce_file(worker_name)
6769
color_stdout('# reproduce file: %s\n' % reproduce_file_path)

0 commit comments

Comments
 (0)