11
2+ import inspect
23import io
34import os
45import sys
@@ -133,7 +134,7 @@ class _TestInfo(object):
133134 # Possible test outcomes
134135 (SUCCESS , FAILURE , ERROR , SKIP ) = range (4 )
135136
136- def __init__ (self , test_result , test_method , outcome = SUCCESS , err = None , subTest = None ):
137+ def __init__ (self , test_result , test_method , outcome = SUCCESS , err = None , subTest = None , filename = None , lineno = None ):
137138 self .test_result = test_result
138139 self .outcome = outcome
139140 self .elapsed_time = 0
@@ -162,6 +163,9 @@ def __init__(self, test_result, test_method, outcome=SUCCESS, err=None, subTest=
162163 self .test_id = subTest .id ()
163164 self .subDescription = subTest ._subDescription ()
164165
166+ self .filename = filename
167+ self .lineno = lineno
168+
165169 def id (self ):
166170 return self .test_id
167171
@@ -211,6 +215,8 @@ def __init__(self, stream=sys.stderr, descriptions=1, verbosity=1,
211215 self .callback = None
212216 self .elapsed_times = elapsed_times
213217 self .properties = properties # junit testsuite properties
218+ self .filename = None
219+ self .lineno = None
214220 if infoclass is None :
215221 self .infoclass = _TestInfo
216222 else :
@@ -222,6 +228,8 @@ def _prepare_callback(self, test_info, target_list, verbose_str,
222228 Appends a `infoclass` to the given target list and sets a callback
223229 method to be called by stopTest method.
224230 """
231+ test_info .filename = self .filename
232+ test_info .lineno = self .lineno
225233 target_list .append (test_info )
226234
227235 def callback ():
@@ -253,6 +261,19 @@ def startTest(self, test):
253261 self .start_time = time ()
254262 TestResult .startTest (self , test )
255263
264+ try :
265+ if getattr (test , '_dt_test' , None ) is not None :
266+ # doctest.DocTestCase
267+ self .filename = test ._dt_test .filename
268+ self .lineno = test ._dt_test .lineno
269+ else :
270+ # regular unittest.TestCase?
271+ test_method = getattr (test , test ._testMethodName )
272+ self .filename = inspect .getsourcefile (test_method )
273+ _ , self .lineno = inspect .getsourcelines (test_method )
274+ finally :
275+ pass
276+
256277 if self .showAll :
257278 self .stream .write (' ' + self .getDescription (test ))
258279 self .stream .write (" ... " )
@@ -534,6 +555,12 @@ def _report_testcase(test_result, xml_testsuite, xml_document):
534555 testcase .setAttribute ('time' , '%.3f' % test_result .elapsed_time )
535556 testcase .setAttribute ('timestamp' , test_result .timestamp )
536557
558+ if test_result .filename is not None :
559+ testcase .setAttribute ('file' , os .path .relpath (test_result .filename ))
560+
561+ if test_result .lineno is not None :
562+ testcase .setAttribute ('line' , str (test_result .lineno ))
563+
537564 if (test_result .outcome != test_result .SUCCESS ):
538565 elem_name = ('failure' , 'error' , 'skipped' )[test_result .outcome - 1 ]
539566 failure = xml_document .createElement (elem_name )
0 commit comments