Commits

Jason Pellerin  committed c625310

Added coverage output file needed by doc builder. Added docs to Failure. Updated Failure to have usable address when possible so that testid can be used to retry. Added tests for that case.

  • Participants
  • Parent commits ad17be0

Comments (0)

Files changed (7)

File functional_tests/doc_tests/test_coverage_html/support/cover/blah.html

+<html>
+<head>
+<title>blah</title>
+</head>
+<body>
+blah
+<style>
+.coverage pre {float: left; margin: 0px 1em; border: none;
+               padding: 0px; line-height: 95% }
+.num pre { margin: 0px }
+.nocov, .nocov pre {background-color: #faa}
+.cov, .cov pre {background-color: #cfc}
+div.coverage div { clear: both; height: 1em}
+</style>
+<div class="stats">
+Covered: 3 lines<br/>
+Missed: 1 lines<br/>
+Skipped 3 lines<br/>
+Percent: 75 %<br/>
+
+</div>
+<div class="coverage">
+<div class="cov"><span class="num"><pre>1</pre></span><pre>def dostuff():</pre></div>
+<div class="cov"><span class="num"><pre>2</pre></span><pre>    print 'hi'</pre></div>
+<div class="skip"><span class="num"><pre>3</pre></span><pre></pre></div>
+<div class="skip"><span class="num"><pre>4</pre></span><pre></pre></div>
+<div class="cov"><span class="num"><pre>5</pre></span><pre>def notcov():</pre></div>
+<div class="nocov"><span class="num"><pre>6</pre></span><pre>    print 'not covered'</pre></div>
+<div class="skip"><span class="num"><pre>7</pre></span><pre></pre></div>
+</div>
+</body>
+</html>

File functional_tests/support/id_fails/test_a.py

+import apackagethatdoesntexist

File functional_tests/support/id_fails/test_b.py

+def test():
+    pass
+
+def test_fail():
+    assert False

File functional_tests/test_withid_failures.rst

+    >>> import os
+    >>> from nose.plugins.plugintest import run_buffered as run
+    >>> from nose.plugins.testid import TestId
+    >>> support = os.path.join(os.path.dirname(__file__), 'support', 'id_fails')
+    >>> argv = [__file__, '-v', '--with-id', support]
+    >>> run(argv=argv, plugins=[TestId()])
+    #1 Failure: ImportError (No module named apackagethatdoesntexist) ... ERROR
+    #2 test_b.test ... ok
+    #3 test_b.test_fail ... FAIL
+    <BLANKLINE>
+    ======================================================================
+    ERROR: Failure: ImportError (No module named apackagethatdoesntexist)
+    ----------------------------------------------------------------------
+    Traceback (most recent call last):
+    ...
+    ImportError: No module named apackagethatdoesntexist
+    <BLANKLINE>
+    ======================================================================
+    FAIL: test_b.test_fail
+    ----------------------------------------------------------------------
+    Traceback (most recent call last):
+    ...
+    AssertionError
+    <BLANKLINE>
+    ----------------------------------------------------------------------
+    Ran 3 tests in ...s
+    <BLANKLINE>
+    FAILED (errors=1, failures=1)
+
+Addressing failures works (sometimes).
+
+    >>> argv.append('1')
+    >>> run(argv=argv, plugins=[TestId()])
+    #1 Failure: ImportError (No module named apackagethatdoesntexist) ... ERROR
+    <BLANKLINE>
+    ======================================================================
+    ERROR: Failure: ImportError (No module named apackagethatdoesntexist)
+    ----------------------------------------------------------------------
+    Traceback (most recent call last):
+    ...
+    ImportError: No module named apackagethatdoesntexist
+    <BLANKLINE>
+    ----------------------------------------------------------------------
+    Ran 1 test in ...s
+    <BLANKLINE>
+    FAILED (errors=1)

File nose/failure.py

 __all__ = ['Failure']
 
 
-# FIXME probably not the best name, since it is mainly used for errors
 class Failure(unittest.TestCase):
+    """Unloadable or unexecutable test.
+
+    A Failure case is placed in a test suite to indicate the presence of a
+    test that could not be loaded or executed. A common example is a test
+    module that fails to import.
+    
+    """
     __test__ = False # do not collect
-    def __init__(self, exc_class, exc_val, tb=None):
+    def __init__(self, exc_class, exc_val, tb=None, address=None):
         log.debug("A failure! %s %s %s", exc_class, exc_val, format_tb(tb))
         self.exc_class = exc_class
         self.exc_val = exc_val
         self.tb = tb
+        self._address = address
         unittest.TestCase.__init__(self)
 
     def __str__(self):
         return "Failure: %s (%s)" % (
             getattr(self.exc_class, '__name__', self.exc_class), self.exc_val)
 
+    def address(self):
+        return self._address
+    
     def runTest(self):
         if self.tb is not None:            
             raise self.exc_class, self.exc_val, self.tb

File nose/loader.py

 from nose.importer import Importer, add_path, remove_path
 from nose.selector import defaultSelector, TestAddress
 from nose.util import cmp_lineno, getpackage, isclass, isgenerator, ispackage, \
-    match_last, resolve_name, transplant_func, transplant_class
+    match_last, resolve_name, transplant_func, transplant_class, test_address
 from nose.suite import ContextSuiteFactory, ContextList, LazySuite
 
 
             raise
         except:
             exc = sys.exc_info()
-            return self.suiteClass([Failure(*exc)])
+            return self.suiteClass(
+                [Failure(exc[0], exc[1], exc[2],
+                         address=(filename, None, None))])
 
     def loadTestsFromGenerator(self, generator, module):
         """Lazy-load tests from a generator function. The generator function
                 raise
             except:
                 exc = sys.exc_info()
-                yield Failure(*exc)
+                yield Failure(exc[0], exc[1], exc[2],
+                              address=test_address(generator))
         return self.suiteClass(generate, context=generator, can_split=False)
 
     def loadTestsFromGeneratorMethod(self, generator, cls):
                 raise
             except:
                 exc = sys.exc_info()
-                yield Failure(*exc)
+                yield Failure(exc[0], exc[1], exc[2],
+                              address=test_address(generator))
         return self.suiteClass(generate, context=generator, can_split=False)
 
     def loadTestsFromModule(self, module, path=None, discovered=False):
                     raise
                 except:
                     exc = sys.exc_info()
-                    return suite([Failure(*exc)])
+                    return suite([Failure(exc[0], exc[1], exc[2],
+                                          address=addr.totuple())])
                 if addr.call:
                     return self.loadTestsFromName(addr.call, module)
                 else:
                             Failure(ValueError,
                                     "Can't find callable %s in file %s: "
                                     "file is not a python module" %
-                                    (addr.call, path))])
+                                    (addr.call, path),
+                                    address=addr.totuple())])
                     return self.loadTestsFromName(addr.call, module=package)
                 else:
                     if op_isdir(path):
                         return self.loadTestsFromFile(path)
                     else:
                         return suite([
-                                Failure(OSError, "No such file %s" % path)])
+                                Failure(OSError, "No such file %s" % path,
+                                        address=addr.totuple())])
             else:
                 # just a function? what to do? I think it can only be
                 # handled when module is not None
                 return suite([
-                    Failure(ValueError, "Unresolvable test name %s" % name)])
+                    Failure(ValueError, "Unresolvable test name %s" % name,
+                            address=addr.totuple())])
 
     def loadTestsFromNames(self, names, module=None):
         """Load tests from all names, returning a suite containing all
         or test suite.
         """
         plug_tests = []
+        try:
+            addr = test_address(obj)
+        except KeyboardInterrupt:
+            raise
+        except:
+            addr = None
         for test in self.config.plugins.makeTest(obj, parent):
             plug_tests.append(test)
         # TODO: is this try/except needed?
         except (KeyboardInterrupt, SystemExit):
             raise
         except:
-            return Failure(*sys.exc_info())
+            exc = sys.exc_info()
+            return Failure(exc[0], exc[1], exc[2], address=addr)
         
         if isinstance(obj, unittest.TestCase):
             return obj
                 return FunctionTestCase(obj)
         else:
             return Failure(TypeError,
-                           "Can't make a test from %s" % obj)
+                           "Can't make a test from %s" % obj,
+                           address=addr)
 
     def resolve(self, name, module):
         """Resolve name within module

File nose/selector.py

             'Final resolution of test name %s: file %s module %s call %s',
             name, self.filename, self.module, self.call)
 
+    def totuple(self):
+        return (self.filename, self.module, self.call)
+        
     def __str__(self):
         return self.name