changeset 28596:9949950664cd

run-tests: add support for automatically bisecting test failures
author Augie Fackler <augie@google.com>
date Sat, 19 Mar 2016 14:26:10 -0400
parents adda6dee600e
children cd34bf29987e
files tests/run-tests.py tests/test-run-tests.t
diffstat 2 files changed, 71 insertions(+), 0 deletions(-) [+]
line wrap: on
line diff
--- a/tests/run-tests.py	Sat Mar 19 20:49:02 2016 -0400
+++ b/tests/run-tests.py	Sat Mar 19 14:26:10 2016 -0400
@@ -270,6 +270,10 @@
                       help='allow extremely slow tests')
     parser.add_option('--showchannels', action='store_true',
                       help='show scheduling channels')
+    parser.add_option('--known-good-rev', type="string",
+                      metavar="known_good_rev",
+                      help=("Automatically bisect any failures using this "
+                            "revision as a known-good revision."))
 
     for option, (envvar, default) in defaults.items():
         defaults[option] = type(default)(os.environ.get(envvar, default))
@@ -1812,6 +1816,40 @@
             self._runner._checkhglib('Tested')
 
             savetimes(self._runner._testdir, result)
+
+            if failed and self._runner.options.known_good_rev:
+                def nooutput(args):
+                    p = subprocess.Popen(args, stderr=subprocess.STDOUT,
+                                         stdout=subprocess.PIPE)
+                    p.stdout.read()
+                    p.wait()
+                for test, msg in result.failures:
+                    nooutput(['hg', 'bisect', '--reset']),
+                    nooutput(['hg', 'bisect', '--bad', '.'])
+                    nooutput(['hg', 'bisect', '--good',
+                              self._runner.options.known_good_rev])
+                    # TODO: we probably need to forward some options
+                    # that alter hg's behavior inside the tests.
+                    rtc = '%s %s %s' % (sys.executable, sys.argv[0], test)
+                    sub = subprocess.Popen(['hg', 'bisect', '--command', rtc],
+                                           stderr=subprocess.STDOUT,
+                                           stdout=subprocess.PIPE)
+                    data = sub.stdout.read()
+                    sub.wait()
+                    m = re.search(
+                        (r'\nThe first (?P<goodbad>bad|good) revision '
+                         r'is:\nchangeset: +\d:(?P<node>[a-f0-9]+)\n.*\n'
+                         r'summary: +(?P<summary>[^\n]+)\n'),
+                        data, (re.MULTILINE | re.DOTALL))
+                    if m is None:
+                        self.stream.writeln(
+                            'Failed to identify failure point for %s' % test)
+                        continue
+                    dat = m.groupdict()
+                    verb = 'broken' if dat['goodbad'] == 'bad' else 'fixed'
+                    self.stream.writeln(
+                        '%s %s by %s (%s)' % (
+                            test, verb, dat['node'], dat['summary']))
             self.stream.writeln(
                 '# Ran %d tests, %d skipped, %d warned, %d failed.'
                 % (result.testsRun,
--- a/tests/test-run-tests.t	Sat Mar 19 20:49:02 2016 -0400
+++ b/tests/test-run-tests.t	Sat Mar 19 14:26:10 2016 -0400
@@ -758,3 +758,36 @@
   $ rt nonlocal/test-is-not-here.t
   .
   # Ran 1 tests, 0 skipped, 0 warned, 0 failed.
+
+support for bisecting failed tests automatically
+  $ hg init bisect
+  $ cd bisect
+  $ cat >> test-bisect.t <<EOF
+  >   $ echo pass
+  >   pass
+  > EOF
+  $ hg add test-bisect.t
+  $ hg ci -m 'good'
+  $ cat >> test-bisect.t <<EOF
+  >   $ echo pass
+  >   fail
+  > EOF
+  $ hg ci -m 'bad'
+  $ rt --known-good-rev=0 test-bisect.t
+  
+  --- $TESTTMP/anothertests/bisect/test-bisect.t
+  +++ $TESTTMP/anothertests/bisect/test-bisect.t.err
+  @@ -1,4 +1,4 @@
+     $ echo pass
+     pass
+     $ echo pass
+  -  fail
+  +  pass
+  
+  ERROR: test-bisect.t output changed
+  !
+  Failed test-bisect.t: output changed
+  test-bisect.t broken by 72cbf122d116 (bad)
+  # Ran 1 tests, 0 skipped, 0 warned, 1 failed.
+  python hash seed: * (glob)
+  [1]