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.
--- 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 = []