run-tests: attempt to fix iolock handling
authorMatt Mackall <mpm@selenic.com>
Mon, 11 Aug 2014 17:45:50 -0500
changeset 22104 70bdf59d27b6
parent 22102 fff8e1cec90f
child 22107 3b5cf39ffcc4
run-tests: attempt to fix iolock handling Ideally, when using -j and -i together, when a prompt comes up, we'd like all other output to wait (but testing to continue!). This gets other output to wait by adding back a bunch of the locking that formerly existed. We switch to a recursive lock to deal with the restructuring due to unittest compatibility. Running tests continue to run, but now the scheduler doesn't schedule any new tasks while waiting at a prompt because no task slots become available due to result output happening in the thread and blocking on the iolock.
tests/run-tests.py
--- a/tests/run-tests.py	Mon Aug 11 11:24:05 2014 -0500
+++ b/tests/run-tests.py	Mon Aug 11 17:45:50 2014 -0500
@@ -1073,7 +1073,7 @@
         output = re.sub(s, r, output)
     return ret, output.splitlines(True)
 
-iolock = threading.Lock()
+iolock = threading.RLock()
 
 class SkipTest(Exception):
     """Raised to indicate that a test is to be skipped."""
@@ -1128,7 +1128,9 @@
             iolock.release()
 
     def addSuccess(self, test):
+        iolock.acquire()
         super(TestResult, self).addSuccess(test)
+        iolock.release()
         self.successes.append(test)
 
     def addError(self, test, err):
@@ -1139,14 +1141,17 @@
     # Polyfill.
     def addSkip(self, test, reason):
         self.skipped.append((test, reason))
+        iolock.acquire()
         if self.showAll:
             self.stream.writeln('skipped %s' % reason)
         else:
             self.stream.write('s')
             self.stream.flush()
+        iolock.release()
 
     def addIgnore(self, test, reason):
         self.ignored.append((test, reason))
+        iolock.acquire()
         if self.showAll:
             self.stream.writeln('ignored %s' % reason)
         else:
@@ -1155,6 +1160,7 @@
             else:
                 self.testsRun += 1
             self.stream.flush()
+        iolock.release()
 
     def addWarn(self, test, reason):
         self.warned.append((test, reason))
@@ -1162,11 +1168,13 @@
         if self._options.first:
             self.stop()
 
+        iolock.acquire()
         if self.showAll:
             self.stream.writeln('warned %s' % reason)
         else:
             self.stream.write('~')
             self.stream.flush()
+        iolock.release()
 
     def addOutputMismatch(self, test, ret, got, expected):
         """Record a mismatch in test output for a particular test."""
@@ -1231,8 +1239,10 @@
         del self._stopped[test.name]
 
         if interrupted:
+            iolock.acquire()
             self.stream.writeln('INTERRUPTED: %s (after %d seconds)' % (
                 test.name, self.times[-1][3]))
+            iolock.release()
 
 class TestSuite(unittest.TestSuite):
     """Custom unitest TestSuite that knows how to execute Mercurial tests."""
@@ -1366,6 +1376,7 @@
         skipped = len(result.skipped)
         ignored = len(result.ignored)
 
+        iolock.acquire()
         self.stream.writeln('')
 
         if not self._runner.options.noskips:
@@ -1418,9 +1429,12 @@
         if self._runner.options.time:
             self.printtimes(result.times)
 
+        iolock.release()
+
         return result
 
     def printtimes(self, times):
+        # iolock held by run
         self.stream.writeln('# Producing time report')
         times.sort(key=lambda t: (t[3]))
         cols = '%7.3f %7.3f %7.3f   %s'