diff options
Diffstat (limited to 'Lib/test/test_doctest.py')
-rw-r--r-- | Lib/test/test_doctest.py | 1004 |
1 files changed, 1002 insertions, 2 deletions
diff --git a/Lib/test/test_doctest.py b/Lib/test/test_doctest.py index fd3426c..68ac44c 100644 --- a/Lib/test/test_doctest.py +++ b/Lib/test/test_doctest.py @@ -1,3 +1,1003 @@ -import doctest +""" +Test script for doctest. +""" + from test import test_support -test_support.run_doctest(doctest) +import doctest + +###################################################################### +## Sample Objects (used by test cases) +###################################################################### + +def sample_func(v): + """ + >>> print sample_func(22) + 44 + """ + return v+v + +class SampleClass: + """ + >>> print 1 + 1 + """ + def __init__(self, val): + """ + >>> print SampleClass(12).get() + 12 + """ + self.val = val + + def double(self): + """ + >>> print SampleClass(12).double().get() + 24 + """ + return SampleClass(self.val + self.val) + + def get(self): + """ + >>> print SampleClass(-5).get() + -5 + """ + return self.val + + def a_staticmethod(v): + """ + >>> print SampleClass.a_staticmethod(10) + 11 + """ + return v+1 + a_staticmethod = staticmethod(a_staticmethod) + + def a_classmethod(cls, v): + """ + >>> print SampleClass.a_classmethod(10) + 12 + >>> print SampleClass(0).a_classmethod(10) + 12 + """ + return v+2 + a_classmethod = classmethod(a_classmethod) + + a_property = property(get, doc=""" + >>> print SampleClass(22).a_property + 22 + """) + + class NestedClass: + """ + >>> x = SampleClass.NestedClass(5) + >>> y = x.square() + >>> print y.get() + 25 + """ + def __init__(self, val=0): + """ + >>> print SampleClass.NestedClass().get() + 0 + """ + self.val = val + def square(self): + return SampleClass.NestedClass(self.val*self.val) + def get(self): + return self.val + +class SampleNewStyleClass(object): + r""" + >>> print '1\n2\n3' + 1 + 2 + 3 + """ + def __init__(self, val): + """ + >>> print SampleNewStyleClass(12).get() + 12 + """ + self.val = val + + def double(self): + """ + >>> print SampleNewStyleClass(12).double().get() + 24 + """ + return SampleNewStyleClass(self.val + self.val) + + def get(self): + """ + >>> print SampleNewStyleClass(-5).get() + -5 + """ + return self.val + +###################################################################### +## Test Cases +###################################################################### + +def test_Example(): r""" +Unit tests for the `Example` class. + +Example is a simple container class that holds a source code string, +an expected output string, and a line number (within the docstring): + + >>> example = doctest.Example('print 1', '1\n', 0) + >>> (example.source, example.want, example.lineno) + ('print 1', '1\n', 0) + +The `source` string should end in a newline iff the source spans more +than one line: + + >>> # Source spans a single line: no terminating newline. + >>> e = doctest.Example('print 1', '1\n', 0) + >>> e = doctest.Example('print 1\n', '1\n', 0) + Traceback (most recent call last): + AssertionError + + >>> # Source spans multiple lines: require terminating newline. + >>> e = doctest.Example('print 1;\nprint 2\n', '1\n2\n', 0) + >>> e = doctest.Example('print 1;\nprint 2', '1\n2\n', 0) + Traceback (most recent call last): + AssertionError + +The `want` string should be terminated by a newline, unless it's the +empty string: + + >>> e = doctest.Example('print 1', '1\n', 0) + >>> e = doctest.Example('print 1', '1', 0) + Traceback (most recent call last): + AssertionError + >>> e = doctest.Example('print', '', 0) +""" + +def test_DocTest(): r""" +Unit tests for the `DocTest` class. + +DocTest is a collection of examples, extracted from a docstring, along +with information about where the docstring comes from (a name, +filename, and line number). The docstring is parsed by the `DocTest` +constructor: + + >>> docstring = ''' + ... >>> print 12 + ... 12 + ... + ... Non-example text. + ... + ... >>> print 'another\example' + ... another + ... example + ... ''' + >>> globs = {} # globals to run the test in. + >>> test = doctest.DocTest(docstring, globs, 'some_test', 'some_file', 20) + >>> print test + <DocTest some_test from some_file:20 (2 examples)> + >>> len(test.examples) + 2 + >>> e1, e2 = test.examples + >>> (e1.source, e1.want, e1.lineno) + ('print 12', '12\n', 1) + >>> (e2.source, e2.want, e2.lineno) + ("print 'another\\example'", 'another\nexample\n', 6) + +Source information (name, filename, and line number) is available as +attributes on the doctest object: + + >>> (test.name, test.filename, test.lineno) + ('some_test', 'some_file', 20) + +The line number of an example within its containing file is found by +adding the line number of the example and the line number of its +containing test: + + >>> test.lineno + e1.lineno + 21 + >>> test.lineno + e2.lineno + 26 + +If the docstring contains inconsistant leading whitespace in the +expected output of an example, then `DocTest` will raise a ValueError: + + >>> docstring = r''' + ... >>> print 'bad\nindentation' + ... bad + ... indentation + ... ''' + >>> doctest.DocTest(docstring, globs, 'some_test', 'filename', 0) + Traceback (most recent call last): + ValueError: line 3 of the docstring for some_test has inconsistent leading whitespace: ' indentation' + +If the docstring contains inconsistent leading whitespace on +continuation lines, then `DocTest` will raise a ValueError: + + >>> docstring = r''' + ... >>> print ('bad indentation', + ... ... 2) + ... ('bad', 'indentation') + ... ''' + >>> doctest.DocTest(docstring, globs, 'some_test', 'filename', 0) + Traceback (most recent call last): + ValueError: line 2 of the docstring for some_test has inconsistent leading whitespace: ' ... 2)' + +If there's no blank space after a PS1 prompt ('>>>'), then `DocTest` +will raise a ValueError: + + >>> docstring = '>>>print 1\n1' + >>> doctest.DocTest(docstring, globs, 'some_test', 'filename', 0) + Traceback (most recent call last): + ValueError: line 0 of the docstring for some_test lacks blank after >>>: '>>>print 1' +""" + +# [XX] test that it's getting line numbers right. +def test_DocTestFinder(): r""" +Unit tests for the `DocTestFinder` class. + +DocTestFinder is used to extract DocTests from an object's docstring +and the docstrings of its contained objects. It can be used with +modules, functions, classes, methods, staticmethods, classmethods, and +properties. + +Finding Tests in Functions +~~~~~~~~~~~~~~~~~~~~~~~~~~ +For a function whose docstring contains examples, DocTestFinder.find() +will return a single test (for that function's docstring): + + >>> # Allow ellipsis in the following examples (since the filename + >>> # and line number in the traceback can vary): + >>> doctest: +ELLIPSIS + + >>> finder = doctest.DocTestFinder() + >>> tests = finder.find(sample_func) + >>> print tests + [<DocTest sample_func from ...:12 (1 example)>] + >>> e = tests[0].examples[0] + >>> print (e.source, e.want, e.lineno) + ('print sample_func(22)', '44\n', 1) + + >>> doctest: -ELLIPSIS # Turn ellipsis back off + +If an object has no docstring, then a test is not created for it: + + >>> def no_docstring(v): + ... pass + >>> finder.find(no_docstring) + [] + +If the function has a docstring with no examples, then a test with no +examples is returned. (This lets `DocTestRunner` collect statistics +about which functions have no tests -- but is that useful? And should +an empty test also be created when there's no docstring?) + + >>> def no_examples(v): + ... ''' no doctest examples ''' + >>> finder.find(no_examples) + [<DocTest no_examples from None:1 (no examples)>] + +Finding Tests in Classes +~~~~~~~~~~~~~~~~~~~~~~~~ +For a class, DocTestFinder will create a test for the class's +docstring, and will recursively explore its contents, including +methods, classmethods, staticmethods, properties, and nested classes. + + >>> finder = doctest.DocTestFinder() + >>> tests = finder.find(SampleClass) + >>> tests.sort() + >>> for t in tests: + ... print '%2s %s' % (len(t.examples), t.name) + 1 SampleClass + 3 SampleClass.NestedClass + 1 SampleClass.NestedClass.__init__ + 1 SampleClass.__init__ + 2 SampleClass.a_classmethod + 1 SampleClass.a_property + 1 SampleClass.a_staticmethod + 1 SampleClass.double + 1 SampleClass.get + +New-style classes are also supported: + + >>> tests = finder.find(SampleNewStyleClass) + >>> tests.sort() + >>> for t in tests: + ... print '%2s %s' % (len(t.examples), t.name) + 1 SampleNewStyleClass + 1 SampleNewStyleClass.__init__ + 1 SampleNewStyleClass.double + 1 SampleNewStyleClass.get + +Finding Tests in Modules +~~~~~~~~~~~~~~~~~~~~~~~~ +For a module, DocTestFinder will create a test for the class's +docstring, and will recursively explore its contents, including +functions, classes, and the `__test__` dictionary, if it exists: + + >>> # A module + >>> import new + >>> m = new.module('some_module') + >>> def triple(val): + ... ''' + ... >>> print tripple(11) + ... 33 + ... ''' + ... return val*3 + >>> m.__dict__.update({ + ... 'sample_func': sample_func, + ... 'SampleClass': SampleClass, + ... '__doc__': ''' + ... Module docstring. + ... >>> print 'module' + ... module + ... ''', + ... '__test__': { + ... 'd': '>>> print 6\n6\n>>> print 7\n7\n', + ... 'c': triple}}) + + >>> finder = doctest.DocTestFinder() + >>> # Use module=test.test_doctest, to prevent doctest from + >>> # ignoring the objects since they weren't defined in m. + >>> import test.test_doctest + >>> tests = finder.find(m, module=test.test_doctest) + >>> tests.sort() + >>> for t in tests: + ... print '%2s %s' % (len(t.examples), t.name) + 1 some_module + 1 some_module.SampleClass + 3 some_module.SampleClass.NestedClass + 1 some_module.SampleClass.NestedClass.__init__ + 1 some_module.SampleClass.__init__ + 2 some_module.SampleClass.a_classmethod + 1 some_module.SampleClass.a_property + 1 some_module.SampleClass.a_staticmethod + 1 some_module.SampleClass.double + 1 some_module.SampleClass.get + 1 some_module.c + 2 some_module.d + 1 some_module.sample_func + +Duplicate Removal +~~~~~~~~~~~~~~~~~ +If a single object is listed twice (under different names), then tests +will only be generated for it once: + + >>> class TwoNames: + ... '''f() and g() are two names for the same method''' + ... + ... def f(self): + ... ''' + ... >>> print TwoNames().f() + ... f + ... ''' + ... return 'f' + ... + ... g = f # define an alias for f. + + >>> finder = doctest.DocTestFinder() + >>> tests = finder.find(TwoNames, ignore_imports=False) + >>> tests.sort() + >>> print len(tests) + 2 + >>> print tests[0].name + TwoNames + >>> print tests[1].name in ('TwoNames.f', 'TwoNames.g') + True + +Filter Functions +~~~~~~~~~~~~~~~~ +Two filter functions can be used to restrict which objects get +examined: a name-based filter and an object-based filter. + + >>> def namefilter(prefix, base): + ... return base.startswith('a_') + >>> tests = doctest.DocTestFinder(namefilter=namefilter).find(SampleClass) + >>> tests.sort() + >>> for t in tests: + ... print '%2s %s' % (len(t.examples), t.name) + 1 SampleClass + 3 SampleClass.NestedClass + 1 SampleClass.NestedClass.__init__ + 1 SampleClass.__init__ + 1 SampleClass.double + 1 SampleClass.get + + >>> def objfilter(obj): + ... return isinstance(obj, (staticmethod, classmethod)) + >>> tests = doctest.DocTestFinder(objfilter=objfilter).find(SampleClass) + >>> tests.sort() + >>> for t in tests: + ... print '%2s %s' % (len(t.examples), t.name) + 1 SampleClass + 3 SampleClass.NestedClass + 1 SampleClass.NestedClass.__init__ + 1 SampleClass.__init__ + 1 SampleClass.a_property + 1 SampleClass.double + 1 SampleClass.get + +If a given object is filtered out, then none of the objects that it +contains will be added either: + + >>> def namefilter(prefix, base): + ... return base == 'NestedClass' + >>> tests = doctest.DocTestFinder(namefilter=namefilter).find(SampleClass) + >>> tests.sort() + >>> for t in tests: + ... print '%2s %s' % (len(t.examples), t.name) + 1 SampleClass + 1 SampleClass.__init__ + 2 SampleClass.a_classmethod + 1 SampleClass.a_property + 1 SampleClass.a_staticmethod + 1 SampleClass.double + 1 SampleClass.get + +The filter functions apply to contained objects, and *not* to the +object explicitly passed to DocTestFinder: + + >>> def namefilter(prefix, base): + ... return base == 'SampleClass' + >>> tests = doctest.DocTestFinder(namefilter=namefilter).find(SampleClass) + >>> len(tests) + 9 + +Turning off Recursion +~~~~~~~~~~~~~~~~~~~~~ +DocTestFinder can be told not to look for tests in contained objects +using the `recurse` flag: + + >>> tests = doctest.DocTestFinder(recurse=False).find(SampleClass) + >>> tests.sort() + >>> for t in tests: + ... print '%2s %s' % (len(t.examples), t.name) + 1 SampleClass +""" + +class test_DocTestRunner: + def basics(): r""" +Unit tests for the `DocTestRunner` class. + +DocTestRunner is used to run DocTest test cases, and to accumulate +statistics. Here's a simple DocTest case we can use: + + >>> def f(x): + ... ''' + ... >>> x = 12 + ... >>> print x + ... 12 + ... >>> x/2 + ... 6 + ... ''' + >>> test = doctest.DocTestFinder().find(f)[0] + +The main DocTestRunner interface is the `run` method, which runs a +given DocTest case in a given namespace (globs). It returns a tuple +`(f,t)`, where `f` is the number of failed tests and `t` is the number +of tried tests. + + >>> doctest.DocTestRunner(verbose=False).run(test) + (0, 3) + +If any example produces incorrect output, then the test runner reports +the failure and proceeds to the next example: + + >>> def f(x): + ... ''' + ... >>> x = 12 + ... >>> print x + ... 14 + ... >>> x/2 + ... 6 + ... ''' + >>> test = doctest.DocTestFinder().find(f)[0] + >>> doctest.DocTestRunner(verbose=True).run(test) + Trying: x = 12 + Expecting: nothing + ok + Trying: print x + Expecting: 14 + ********************************************************************** + Failure in example: print x + from line #2 of f + Expected: 14 + Got: 12 + Trying: x/2 + Expecting: 6 + ok + (1, 3) +""" + def verbose_flag(): r""" +The `verbose` flag makes the test runner generate more detailed +output: + + >>> def f(x): + ... ''' + ... >>> x = 12 + ... >>> print x + ... 12 + ... >>> x/2 + ... 6 + ... ''' + >>> test = doctest.DocTestFinder().find(f)[0] + + >>> doctest.DocTestRunner(verbose=True).run(test) + Trying: x = 12 + Expecting: nothing + ok + Trying: print x + Expecting: 12 + ok + Trying: x/2 + Expecting: 6 + ok + (0, 3) + +If the `verbose` flag is unspecified, then the output will be verbose +iff `-v` appears in sys.argv: + + >>> # Save the real sys.argv list. + >>> old_argv = sys.argv + + >>> # If -v does not appear in sys.argv, then output isn't verbose. + >>> sys.argv = ['test'] + >>> doctest.DocTestRunner().run(test) + (0, 3) + + >>> # If -v does appear in sys.argv, then output is verbose. + >>> sys.argv = ['test', '-v'] + >>> doctest.DocTestRunner().run(test) + Trying: x = 12 + Expecting: nothing + ok + Trying: print x + Expecting: 12 + ok + Trying: x/2 + Expecting: 6 + ok + (0, 3) + + >>> # Restore sys.argv + >>> sys.argv = old_argv + +In the remaining examples, the test runner's verbosity will be +explicitly set, to ensure that the test behavior is consistent. + """ + def exceptions(): r""" +Tests of `DocTestRunner`'s exception handling. + +An expected exception is specified with a traceback message. The +lines between the first line and the type/value may be omitted or +replaced with any other string: + + >>> def f(x): + ... ''' + ... >>> x = 12 + ... >>> print x/0 + ... Traceback (most recent call last): + ... ZeroDivisionError: integer division or modulo by zero + ... ''' + >>> test = doctest.DocTestFinder().find(f)[0] + >>> doctest.DocTestRunner(verbose=False).run(test) + (0, 2) + +An example may generate output before it raises an exception; if it +does, then the output must match the expected output: + + >>> def f(x): + ... ''' + ... >>> x = 12 + ... >>> print 'pre-exception output', x/0 + ... pre-exception output + ... Traceback (most recent call last): + ... ZeroDivisionError: integer division or modulo by zero + ... ''' + >>> test = doctest.DocTestFinder().find(f)[0] + >>> doctest.DocTestRunner(verbose=False).run(test) + (0, 2) + +Exception messages may contain newlines: + + >>> def f(x): + ... r''' + ... >>> raise ValueError, 'multi\nline\nmessage' + ... Traceback (most recent call last): + ... ValueError: multi + ... line + ... message + ... ''' + >>> test = doctest.DocTestFinder().find(f)[0] + >>> doctest.DocTestRunner(verbose=False).run(test) + (0, 1) + +If an exception is expected, but an exception with the wrong type or +message is raised, then it is reported as a failure: + + >>> def f(x): + ... r''' + ... >>> raise ValueError, 'message' + ... Traceback (most recent call last): + ... ValueError: wrong message + ... ''' + >>> test = doctest.DocTestFinder().find(f)[0] + >>> doctest.DocTestRunner(verbose=False).run(test) + ********************************************************************** + Failure in example: raise ValueError, 'message' + from line #1 of f + Expected: + Traceback (most recent call last): + ValueError: wrong message + Got: + Traceback (most recent call last): + ValueError: message + (1, 1) + +If an exception is raised but not expected, then it is reported as an +unexpected exception: + + >>> # Allow ellipsis in the following examples (since the filename + >>> # and line number in the traceback can vary): + >>> doctest: +ELLIPSIS + + >>> def f(x): + ... r''' + ... >>> 1/0 + ... 0 + ... ''' + >>> test = doctest.DocTestFinder().find(f)[0] + >>> doctest.DocTestRunner(verbose=False).run(test) + ********************************************************************** + Failure in example: 1/0 + from line #1 of f + Exception raised: + Traceback (most recent call last): + ... + ZeroDivisionError: integer division or modulo by zero + (1, 1) + + >>> doctest: -ELLIPSIS # Turn ellipsis back off: +""" + def optionflags(): r""" +Tests of `DocTestRunner`'s option flag handling. + +Several option flags can be used to customize the behavior of the test +runner. These are defined as module constants in doctest, and passed +to the DocTestRunner constructor (multiple constants should be or-ed +together). + +The DONT_ACCEPT_TRUE_FOR_1 flag disables matches between True/False +and 1/0: + + >>> def f(x): + ... '>>> True\n1\n' + + >>> # Without the flag: + >>> test = doctest.DocTestFinder().find(f)[0] + >>> doctest.DocTestRunner(verbose=False).run(test) + (0, 1) + + >>> # With the flag: + >>> test = doctest.DocTestFinder().find(f)[0] + >>> flags = doctest.DONT_ACCEPT_TRUE_FOR_1 + >>> doctest.DocTestRunner(verbose=False, optionflags=flags).run(test) + ********************************************************************** + Failure in example: True + from line #0 of f + Expected: 1 + Got: True + (1, 1) + +The DONT_ACCEPT_BLANKLINE flag disables the match between blank lines +and the '<BLANKLINE>' marker: + + >>> def f(x): + ... '>>> print "a\\n\\nb"\na\n<BLANKLINE>\nb\n' + + >>> # Without the flag: + >>> test = doctest.DocTestFinder().find(f)[0] + >>> doctest.DocTestRunner(verbose=False).run(test) + (0, 1) + + >>> # With the flag: + >>> test = doctest.DocTestFinder().find(f)[0] + >>> flags = doctest.DONT_ACCEPT_BLANKLINE + >>> doctest.DocTestRunner(verbose=False, optionflags=flags).run(test) + ********************************************************************** + Failure in example: print "a\n\nb" + from line #0 of f + Expected: + a + <BLANKLINE> + b + Got: + a + <BLANKLINE> + b + (1, 1) + +The NORMALIZE_WHITESPACE flag causes all sequences of whitespace to be +treated as equal: + + >>> def f(x): + ... '>>> print 1, 2, 3\n 1 2\n 3' + + >>> # Without the flag: + >>> test = doctest.DocTestFinder().find(f)[0] + >>> doctest.DocTestRunner(verbose=False).run(test) + ********************************************************************** + Failure in example: print 1, 2, 3 + from line #0 of f + Expected: + 1 2 + 3 + Got: 1 2 3 + (1, 1) + + >>> # With the flag: + >>> test = doctest.DocTestFinder().find(f)[0] + >>> flags = doctest.NORMALIZE_WHITESPACE + >>> doctest.DocTestRunner(verbose=False, optionflags=flags).run(test) + (0, 1) + +The ELLIPSIS flag causes ellipsis marker ("...") in the expected +output to match any substring in the actual output: + + >>> def f(x): + ... '>>> print range(15)\n[0, 1, 2, ..., 14]\n' + + >>> # Without the flag: + >>> test = doctest.DocTestFinder().find(f)[0] + >>> doctest.DocTestRunner(verbose=False).run(test) + ********************************************************************** + Failure in example: print range(15) + from line #0 of f + Expected: [0, 1, 2, ..., 14] + Got: [0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14] + (1, 1) + + >>> # With the flag: + >>> test = doctest.DocTestFinder().find(f)[0] + >>> flags = doctest.ELLIPSIS + >>> doctest.DocTestRunner(verbose=False, optionflags=flags).run(test) + (0, 1) + +The UNIFIED_DIFF flag causes failures that involve multi-line expected +and actual outputs to be displayed using a unified diff: + + >>> def f(x): + ... r''' + ... >>> print '\n'.join('abcdefg') + ... a + ... B + ... c + ... d + ... f + ... g + ... h + ... ''' + + >>> # Without the flag: + >>> test = doctest.DocTestFinder().find(f)[0] + >>> doctest.DocTestRunner(verbose=False).run(test) + ********************************************************************** + Failure in example: print '\n'.join('abcdefg') + from line #1 of f + Expected: + a + B + c + d + f + g + h + Got: + a + b + c + d + e + f + g + (1, 1) + + >>> # With the flag: + >>> test = doctest.DocTestFinder().find(f)[0] + >>> flags = doctest.UNIFIED_DIFF + >>> doctest.DocTestRunner(verbose=False, optionflags=flags).run(test) + ********************************************************************** + Failure in example: print '\n'.join('abcdefg') + from line #1 of f + Differences (unified diff): + --- Expected + +++ Got + @@ -1,8 +1,8 @@ + a + -B + +b + c + d + +e + f + g + -h + <BLANKLINE> + (1, 1) + +The CONTEXT_DIFF flag causes failures that involve multi-line expected +and actual outputs to be displayed using a context diff: + + >>> # Reuse f() from the UNIFIED_DIFF example, above. + >>> test = doctest.DocTestFinder().find(f)[0] + >>> flags = doctest.CONTEXT_DIFF + >>> doctest.DocTestRunner(verbose=False, optionflags=flags).run(test) + ********************************************************************** + Failure in example: print '\n'.join('abcdefg') + from line #1 of f + Differences (context diff): + *** Expected + --- Got + *************** + *** 1,8 **** + a + ! B + c + d + f + g + - h + <BLANKLINE> + --- 1,8 ---- + a + ! b + c + d + + e + f + g + <BLANKLINE> + (1, 1) +""" + def option_directives(): r""" +Tests of `DocTestRunner`'s option directive mechanism. + +Option directives can be used to turn option flags on or off from +within a DocTest case. The following example shows how a flag can be +turned on and off. Note that comments on the same line as the option +directive are ignored. + + >>> def f(x): r''' + ... >>> print range(10) # Should fail: no ellipsis + ... [0, 1, ..., 9] + ... + ... >>> doctest: +ELLIPSIS # turn ellipsis on. + ... >>> print range(10) # Should succeed + ... [0, 1, ..., 9] + ... + ... >>> doctest: -ELLIPSIS # turn ellipsis back off. + ... >>> print range(10) # Should fail: no ellipsis + ... [0, 1, ..., 9] + ... ''' + >>> test = doctest.DocTestFinder().find(f)[0] + >>> doctest.DocTestRunner(verbose=False).run(test) + ********************************************************************** + Failure in example: print range(10) # Should fail: no ellipsis + from line #1 of f + Expected: [0, 1, ..., 9] + Got: [0, 1, 2, 3, 4, 5, 6, 7, 8, 9] + ********************************************************************** + Failure in example: print range(10) # Should fail: no ellipsis + from line #9 of f + Expected: [0, 1, ..., 9] + Got: [0, 1, 2, 3, 4, 5, 6, 7, 8, 9] + (2, 3) + +Multiple flags can be toggled by a single option directive: + + >>> def f(x): r''' + ... >>> print range(10) # Should fail + ... [0, 1, ..., 9] + ... >>> doctest: +ELLIPSIS +NORMALIZE_WHITESPACE + ... >>> print range(10) # Should succeed + ... [0, 1, ..., 9] + ... ''' + >>> test = doctest.DocTestFinder().find(f)[0] + >>> doctest.DocTestRunner(verbose=False).run(test) + ********************************************************************** + Failure in example: print range(10) # Should fail + from line #1 of f + Expected: [0, 1, ..., 9] + Got: [0, 1, 2, 3, 4, 5, 6, 7, 8, 9] + (1, 2) +""" + +def test_testsource(): r""" +Unit tests for `testsource()`. + +The testsource() function takes a module and a name, finds the (first) +test with that name in that module, and converts it to an + + >>> import test.test_doctest + >>> name = 'test.test_doctest.sample_func' + >>> print doctest.testsource(test.test_doctest, name) + print sample_func(22) + # Expected: + # 44 + + >>> name = 'test.test_doctest.SampleNewStyleClass' + >>> print doctest.testsource(test.test_doctest, name) + print '1\n2\n3' + # Expected: + # 1 + # 2 + # 3 + + >>> name = 'test.test_doctest.SampleClass.a_classmethod' + >>> print doctest.testsource(test.test_doctest, name) + print SampleClass.a_classmethod(10) + # Expected: + # 12 + print SampleClass(0).a_classmethod(10) + # Expected: + # 12 +""" + +def test_debug(): r""" + +Create a docstring that we want to debug: + + >>> s = ''' + ... >>> x = 12 + ... >>> print x + ... 12 + ... ''' + +Create some fake stdin input, to feed to the debugger: + + >>> import tempfile + >>> fake_stdin = tempfile.TemporaryFile(mode='w+') + >>> fake_stdin.write('\n'.join(['next', 'print x', 'continue', ''])) + >>> fake_stdin.seek(0) + >>> real_stdin = sys.stdin + >>> sys.stdin = fake_stdin + +Run the debugger on the docstring, and then restore sys.stdin. + + >>> doctest: +NORMALIZE_WHITESPACE + >>> try: + ... doctest.debug_src(s) + ... finally: + ... sys.stdin = real_stdin + ... fake_stdin.close() + > <string>(1)?() + (Pdb) 12 + --Return-- + > <string>(1)?()->None + (Pdb) 12 + (Pdb) + +""" + +###################################################################### +## Main +###################################################################### + +def test_main(): + # Check the doctest cases in doctest itself: + test_support.run_doctest(doctest, verbosity=True) + # Check the doctest cases defined here: + from test import test_doctest + test_support.run_doctest(test_doctest, verbosity=True) + +import trace, sys, re, StringIO +def test_coverage(coverdir): + tracer = trace.Trace(ignoredirs=[sys.prefix, sys.exec_prefix,], + trace=0, count=1) + tracer.run('reload(doctest); test_main()') + r = tracer.results() + print 'Writing coverage results...' + r.write_results(show_missing=True, summary=True, + coverdir=coverdir) + +if __name__ == '__main__': + if '-c' in sys.argv: + test_coverage('/tmp/doctest.cover') + else: + test_main() |