tests: add support for inline doctests in test files
authorIdan Kamara <idankk86@gmail.com>
Wed, 12 Oct 2011 22:01:14 +0200
changeset 15236 4fae5df4b1bb
parent 15235 f7044da7a793
child 15237 7196ed7a1505
tests: add support for inline doctests in test files This adds doctest like syntax to .t files, that can be interleaved with regular shell code: $ echo -n a > file >>> print open('file').read() a >>> open('file', 'a').write('b') $ cat file ab The syntax is exactly the same as regular doctests, so multiline statements look like this: >>> for i in range(3): ... print i 0 1 2 Each block has its own context, i.e.: >>> x = 0 >>> print x 0 $ echo 'foo' foo >>> print x will result in a NameError. Errors are displayed in standard doctest format: >>> print 'foo' bar --- /home/idan/dev/hg/default/tests/test-test.t +++ /home/idan/dev/hg/default/tests/test-test.t.err @@ -2,3 +2,16 @@ > >>> print 'foo' > bar > EOF + ********************************************************************** + File "/tmp/tmps8X_0ohg-tst", line 1, in tmps8X_0ohg-tst + Failed example: + print 'foo' + Expected: + bar + Got: + foo + ********************************************************************** + 1 items had failures: + 1 of 1 in tmps8X_0ohg-tst + ***Test Failed*** 1 failures. + [1] As for the implementation, it's quite simple: when the test runner sees a line starting with '>>>' it converts it, and all subsequent lines until the next line that begins with '$' to a 'python -m heredoctest <<EOF' call with the proper heredoc to follow. So if we have this test file: >>> for c in 'abcd': ... print c a b c d $ echo foo foo It gets converted to: $ python -m heredoctest <<EOF > >>> for c in 'abcd': > ... print c > a > b > c > d > EOF $ echo foo foo And then processed like every other test file by converting it to a sh script.
tests/run-tests.py
--- a/tests/run-tests.py	Wed Oct 12 22:01:13 2011 +0200
+++ b/tests/run-tests.py	Wed Oct 12 22:01:14 2011 +0200
@@ -521,6 +521,26 @@
 def stringescape(s):
     return escapesub(escapef, s)
 
+def transformtst(lines):
+    inblock = False
+    for l in lines:
+        if inblock:
+            if l.startswith('  $ '):
+                inblock = False
+                yield '  > EOF\n'
+                yield l
+            else:
+                yield '  > ' + l[2:]
+        else:
+            if l.startswith('  >>> '):
+                inblock = True
+                yield '  $ %s -m heredoctest <<EOF\n' % PYTHON
+                yield '  > ' + l[2:]
+            else:
+                yield l
+    if inblock:
+        yield '  > EOF\n'
+
 def tsttest(test, wd, options, replacements):
     t = open(test)
     out = []
@@ -530,7 +550,7 @@
     pos = prepos = -1
     after = {}
     expected = {}
-    for n, l in enumerate(t):
+    for n, l in enumerate(transformtst(t)):
         if not l.endswith('\n'):
             l += '\n'
         if l.startswith('  $ '): # commands
@@ -833,7 +853,7 @@
         refout = None                   # to match "out is None"
     elif os.path.exists(ref):
         f = open(ref, "r")
-        refout = splitnewlines(f.read())
+        refout = list(transformtst(splitnewlines(f.read())))
         f.close()
     else:
         refout = []