Patchwork [1,of,3] run-tests: do not prompt changes (-i) if a race condition is detected

login
register
mail settings
Submitter Jun Wu
Date June 21, 2017, 8:13 a.m.
Message ID <59730060864e32202271.1498032799@x1c>
Download mbox | patch
Permalink /patch/21582/
State Accepted
Headers show

Comments

Jun Wu - June 21, 2017, 8:13 a.m.
# HG changeset patch
# User Jun Wu <quark@fb.com>
# Date 1498032320 25200
#      Wed Jun 21 01:05:20 2017 -0700
# Node ID 59730060864e3220227133cb9cd036720a329935
# Parent  0ce2cbebd74964ffe61e79de8941461bccc9371b
# Available At https://bitbucket.org/quark-zju/hg-draft
#              hg pull https://bitbucket.org/quark-zju/hg-draft -r 59730060864e
run-tests: do not prompt changes (-i) if a race condition is detected

The race condition is like:

  1. run-tests.py reads test-a.t as reference output, content A
  2. run-tests.py runs the test (which could be content B, another race
     condition fixed by the next patch, but assume it's content A here)
  3. something changes test-a.t to content C
  4. run-tests.py compares test output (content D) with content A
  5. with "-i", run-tests.py prompts diff(A, D), while the file has content
     C instead of A at this time

This patch detects the above case and tell the user to rerun the test if
they want to apply test changes.

Patch

diff --git a/tests/run-tests.py b/tests/run-tests.py
--- a/tests/run-tests.py
+++ b/tests/run-tests.py
@@ -634,14 +634,17 @@  class Test(unittest.TestCase):
         self._chgsockdir = None
 
+        self._refout = self.readrefout()
+
+    def readrefout(self):
+        """read reference output"""
         # If we're not in --debug mode and reference output file exists,
         # check test output against it.
-        if debug:
-            self._refout = None # to match "out is None"
+        if self._debug:
+            return None # to match "out is None"
         elif os.path.exists(self.refpath):
-            f = open(self.refpath, 'rb')
-            self._refout = f.read().splitlines(True)
-            f.close()
+            with open(self.refpath, 'rb') as f:
+                return f.read().splitlines(True)
         else:
-            self._refout = []
+            return []
 
     # needed to get base class __repr__ running
@@ -1589,12 +1592,17 @@  class TestResult(unittest._TextTestResul
             # handle interactive prompt without releasing iolock
             if self._options.interactive:
-                self.stream.write('Accept this change? [n] ')
-                answer = sys.stdin.readline().strip()
-                if answer.lower() in ('y', 'yes'):
-                    if test.name.endswith('.t'):
-                        rename(test.errpath, test.path)
-                    else:
-                        rename(test.errpath, '%s.out' % test.path)
-                    accepted = True
+                if test.readrefout() != expected:
+                    self.stream.write(
+                        'Reference output has changed (run again to prompt '
+                        'changes)')
+                else:
+                    self.stream.write('Accept this change? [n] ')
+                    answer = sys.stdin.readline().strip()
+                    if answer.lower() in ('y', 'yes'):
+                        if test.name.endswith('.t'):
+                            rename(test.errpath, test.path)
+                        else:
+                            rename(test.errpath, '%s.out' % test.path)
+                        accepted = True
             if not accepted:
                 self.faildata[test.name] = b''.join(lines)
diff --git a/tests/test-run-tests.t b/tests/test-run-tests.t
--- a/tests/test-run-tests.t
+++ b/tests/test-run-tests.t
@@ -642,4 +642,31 @@  Accept the fix
     saved backup bundle to $TESTTMP/*.hg (glob)<
 
+Race condition - test file was modified when test is running
+
+  $ TESTRACEDIR=`pwd`
+  $ export TESTRACEDIR
+  $ cat > test-race.t <<EOF
+  >   $ echo 1
+  >   $ echo "# a new line" >> $TESTRACEDIR/test-race.t
+  > EOF
+
+  $ rt -i test-race.t
+  
+  --- $TESTTMP/test-race.t
+  +++ $TESTTMP/test-race.t.err
+  @@ -1,2 +1,3 @@
+     $ echo 1
+  +  1
+     $ echo "# a new line" >> $TESTTMP/test-race.t
+  Reference output has changed (run again to prompt changes)
+  ERROR: test-race.t output changed
+  !
+  Failed test-race.t: output changed
+  # Ran 1 tests, 0 skipped, 1 failed.
+  python hash seed: * (glob)
+  [1]
+
+  $ rm test-race.t
+
 (reinstall)
   $ mv backup test-failure.t