# HG changeset patch # User Idan Kamara # Date 2011-10-12 20:01:14 # Node ID 4fae5df4b1bb1969bb90b1f156b49cd401ecabe5 # Parent f7044da7a79353b9c1a71f0ef906546467e470eb 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 <>> for c in 'abcd': ... print c a b c d $ echo foo foo It gets converted to: $ python -m heredoctest < >>> 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. diff --git a/tests/run-tests.py b/tests/run-tests.py --- a/tests/run-tests.py +++ b/tests/run-tests.py @@ -521,6 +521,26 @@ def escapef(m): 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 < ' + l[2:] + else: + yield l + if inblock: + yield ' > EOF\n' + def tsttest(test, wd, options, replacements): t = open(test) out = [] @@ -530,7 +550,7 @@ def tsttest(test, wd, options, replaceme 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 @@ def runone(options, test): 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 = []