2010-03-30 00:25:25 +04:00
# Python module for parsing and generating the Subunit protocol
# (Samba-specific)
# Copyright (C) 2008-2009 Jelmer Vernooij <jelmer@samba.org>
#
# This program is free software; you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation; either version 3 of the License, or
# (at your option) any later version.
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
# You should have received a copy of the GNU General Public License
# along with this program. If not, see <http://www.gnu.org/licenses/>.
__all__ = [ ' parse_results ' ]
import re
2010-03-30 14:46:26 +04:00
import sys
2010-03-30 16:36:25 +04:00
import subunit
2010-09-13 23:31:08 +04:00
import subunit . iso8601
2010-09-13 22:53:54 +04:00
import testtools
2010-03-30 00:25:25 +04:00
VALID_RESULTS = [ ' success ' , ' successful ' , ' failure ' , ' fail ' , ' skip ' , ' knownfail ' , ' error ' , ' xfail ' , ' skip-testsuite ' , ' testsuite-failure ' , ' testsuite-xfail ' , ' testsuite-success ' , ' testsuite-error ' ]
2010-09-13 23:17:05 +04:00
class TestsuiteEnabledTestResult ( testtools . testresult . TestResult ) :
def start_testsuite ( self , name ) :
raise NotImplementedError ( self . start_testsuite )
2010-03-30 00:25:25 +04:00
def parse_results ( msg_ops , statistics , fh ) :
expected_fail = 0
2010-09-14 00:09:46 +04:00
open_tests = { }
2010-03-30 00:25:25 +04:00
while fh :
l = fh . readline ( )
2010-03-30 02:30:52 +04:00
if l == " " :
break
2010-04-11 03:39:06 +04:00
parts = l . split ( None , 1 )
if not len ( parts ) == 2 or not l . startswith ( parts [ 0 ] ) :
2010-08-26 04:25:44 +04:00
msg_ops . output_msg ( l )
2010-04-11 03:39:06 +04:00
continue
command = parts [ 0 ] . rstrip ( " : " )
arg = parts [ 1 ]
if command in ( " test " , " testing " ) :
msg_ops . control_msg ( l )
2010-09-14 00:09:46 +04:00
name = arg . rstrip ( )
test = subunit . RemotedTestCase ( name )
if name in open_tests :
2010-09-14 05:47:04 +04:00
msg_ops . addError ( open_tests . pop ( name ) , subunit . RemoteError ( u " Test already running " ) )
2010-09-14 00:09:46 +04:00
msg_ops . startTest ( test )
open_tests [ name ] = test
2010-04-11 03:39:06 +04:00
elif command == " time " :
2010-03-30 00:25:25 +04:00
msg_ops . control_msg ( l )
2010-09-13 23:31:08 +04:00
try :
dt = subunit . iso8601 . parse_date ( arg . rstrip ( " \n " ) )
except TypeError , e :
print " Unable to parse time line: %s " % arg . rstrip ( " \n " )
else :
msg_ops . time ( dt )
2010-04-11 03:39:06 +04:00
elif command in VALID_RESULTS :
2010-03-30 00:25:25 +04:00
msg_ops . control_msg ( l )
2010-04-11 03:39:06 +04:00
result = command
grp = re . match ( " (.*?)( \ [)?([ \t ]*)( multipart)? \n " , arg )
( testname , hasreason ) = ( grp . group ( 1 ) , grp . group ( 2 ) )
2010-03-30 00:25:25 +04:00
if hasreason :
reason = " "
# reason may be specified in next lines
terminated = False
while fh :
l = fh . readline ( )
2010-03-30 02:30:52 +04:00
if l == " " :
break
2010-03-30 00:25:25 +04:00
msg_ops . control_msg ( l )
if l == " ] \n " :
terminated = True
break
else :
reason + = l
2010-08-26 05:50:08 +04:00
2010-09-14 05:47:04 +04:00
remote_error = subunit . RemoteError ( reason . decode ( " utf-8 " ) )
2010-03-30 00:25:25 +04:00
if not terminated :
statistics [ ' TESTS_ERROR ' ] + = 1
2010-09-14 05:47:04 +04:00
msg_ops . addError ( subunit . RemotedTestCase ( testname ) , subunit . RemoteError ( u " reason ( %s ) interrupted " % result ) )
2010-03-30 00:25:25 +04:00
return 1
2010-03-30 02:30:52 +04:00
else :
reason = None
2010-09-14 05:47:04 +04:00
remote_error = subunit . RemoteError ( u " No reason specified " )
2010-03-30 00:25:25 +04:00
if result in ( " success " , " successful " ) :
2010-04-11 00:35:57 +04:00
try :
2010-09-14 00:09:46 +04:00
test = open_tests . pop ( testname )
except KeyError :
2010-04-11 00:35:57 +04:00
statistics [ ' TESTS_ERROR ' ] + = 1
2010-09-14 05:47:04 +04:00
msg_ops . addError ( subunit . RemotedTestCase ( testname ) , subunit . RemoteError ( u " Test was never started " ) )
2010-04-11 00:35:57 +04:00
else :
statistics [ ' TESTS_EXPECTED_OK ' ] + = 1
2010-09-14 05:47:04 +04:00
msg_ops . addSuccess ( test )
2010-03-30 00:25:25 +04:00
elif result in ( " xfail " , " knownfail " ) :
2010-04-11 00:35:57 +04:00
try :
2010-09-14 00:09:46 +04:00
test = open_tests . pop ( testname )
except KeyError :
2010-04-11 00:35:57 +04:00
statistics [ ' TESTS_ERROR ' ] + = 1
2010-09-14 05:47:04 +04:00
msg_ops . addError ( subunit . RemotedTestCase ( testname ) , subunit . RemoteError ( u " Test was never started " ) )
2010-04-11 00:35:57 +04:00
else :
statistics [ ' TESTS_EXPECTED_FAIL ' ] + = 1
2010-09-14 05:47:04 +04:00
msg_ops . addExpectedFailure ( test , remote_error )
2010-04-11 00:35:57 +04:00
expected_fail + = 1
2010-03-30 00:25:25 +04:00
elif result in ( " failure " , " fail " ) :
2010-04-11 00:35:57 +04:00
try :
2010-09-14 00:09:46 +04:00
test = open_tests . pop ( testname )
except KeyError :
2010-04-11 00:35:57 +04:00
statistics [ ' TESTS_ERROR ' ] + = 1
2010-09-14 05:47:04 +04:00
msg_ops . addError ( subunit . RemotedTestCase ( testname ) , subunit . RemoteError ( u " Test was never started " ) )
2010-04-11 00:35:57 +04:00
else :
statistics [ ' TESTS_UNEXPECTED_FAIL ' ] + = 1
2010-09-14 05:47:04 +04:00
msg_ops . addFailure ( test , remote_error )
2010-03-30 00:25:25 +04:00
elif result == " skip " :
statistics [ ' TESTS_SKIP ' ] + = 1
# Allow tests to be skipped without prior announcement of test
2010-09-14 00:09:46 +04:00
try :
test = open_tests . pop ( testname )
except KeyError :
test = subunit . RemotedTestCase ( testname )
msg_ops . addSkip ( test , reason )
2010-03-30 00:25:25 +04:00
elif result == " error " :
statistics [ ' TESTS_ERROR ' ] + = 1
2010-04-11 00:35:57 +04:00
try :
2010-09-14 00:09:46 +04:00
test = open_tests . pop ( testname )
except KeyError :
test = subunit . RemotedTestCase ( testname )
2010-09-14 05:47:04 +04:00
msg_ops . addError ( test , remote_error )
2010-03-30 00:25:25 +04:00
elif result == " skip-testsuite " :
msg_ops . skip_testsuite ( testname )
elif result == " testsuite-success " :
msg_ops . end_testsuite ( testname , " success " , reason )
elif result == " testsuite-failure " :
msg_ops . end_testsuite ( testname , " failure " , reason )
elif result == " testsuite-xfail " :
msg_ops . end_testsuite ( testname , " xfail " , reason )
elif result == " testsuite-error " :
msg_ops . end_testsuite ( testname , " error " , reason )
2010-08-26 04:25:44 +04:00
else :
raise AssertionError ( " Recognized but unhandled result %r " %
result )
2010-04-11 03:39:06 +04:00
elif command == " testsuite " :
msg_ops . start_testsuite ( arg . strip ( ) )
elif command == " progress " :
arg = arg . strip ( )
2010-03-30 16:36:25 +04:00
if arg == " pop " :
msg_ops . progress ( None , subunit . PROGRESS_POP )
elif arg == " push " :
msg_ops . progress ( None , subunit . PROGRESS_PUSH )
elif arg [ 0 ] in ' +- ' :
msg_ops . progress ( int ( arg ) , subunit . PROGRESS_CUR )
else :
msg_ops . progress ( int ( arg ) , subunit . PROGRESS_SET )
2010-03-30 00:25:25 +04:00
else :
msg_ops . output_msg ( l )
while open_tests :
2010-09-14 00:13:15 +04:00
test = subunit . RemotedTestCase ( open_tests . popitem ( ) [ 1 ] )
2010-09-14 05:47:04 +04:00
msg_ops . addError ( test , subunit . RemoteError ( u " was started but never finished! " ) )
2010-03-30 00:25:25 +04:00
statistics [ ' TESTS_ERROR ' ] + = 1
if statistics [ ' TESTS_ERROR ' ] > 0 :
return 1
if statistics [ ' TESTS_UNEXPECTED_FAIL ' ] > 0 :
2010-09-14 05:47:04 +04:00
return 1
2010-03-30 00:25:25 +04:00
return 0
2010-09-13 23:17:05 +04:00
class SubunitOps ( subunit . TestProtocolClient , TestsuiteEnabledTestResult ) :
2010-03-30 00:25:25 +04:00
2010-03-30 02:59:04 +04:00
# The following are Samba extensions:
def start_testsuite ( self , name ) :
2010-09-13 23:17:05 +04:00
self . _stream . write ( " testsuite: %s \n " % name )
2010-03-30 00:25:25 +04:00
2010-03-30 02:59:04 +04:00
def skip_testsuite ( self , name , reason = None ) :
if reason :
2010-09-13 23:17:05 +04:00
self . _stream . write ( " skip-testsuite: %s [ \n %s \n ] \n " % ( name , reason ) )
2010-03-30 02:59:04 +04:00
else :
2010-09-13 23:17:05 +04:00
self . _stream . write ( " skip-testsuite: %s \n " % name )
2010-03-30 00:25:25 +04:00
2010-03-30 02:59:04 +04:00
def end_testsuite ( self , name , result , reason = None ) :
if reason :
2010-09-13 23:17:05 +04:00
self . _stream . write ( " testsuite- %s : %s [ \n %s \n ] \n " % ( result , name , reason ) )
2010-03-30 02:59:04 +04:00
else :
2010-09-13 23:17:05 +04:00
self . _stream . write ( " testsuite- %s : %s \n " % ( result , name ) )
2010-03-30 00:25:25 +04:00
2010-03-30 14:46:26 +04:00
def read_test_regexes ( name ) :
2010-04-06 05:55:10 +04:00
ret = { }
2010-03-30 14:46:26 +04:00
f = open ( name , ' r ' )
try :
for l in f :
l = l . strip ( )
if l == " " or l [ 0 ] == " # " :
continue
if " # " in l :
( regex , reason ) = l . split ( " # " , 1 )
2010-04-06 05:55:10 +04:00
ret [ regex . strip ( ) ] = reason . strip ( )
2010-03-30 14:46:26 +04:00
else :
2010-04-06 05:55:10 +04:00
ret [ l ] = None
2010-03-30 14:46:26 +04:00
finally :
f . close ( )
2010-04-06 05:55:10 +04:00
return ret
2010-03-30 14:46:26 +04:00
def find_in_list ( regexes , fullname ) :
2010-04-06 05:55:10 +04:00
for regex , reason in regexes . iteritems ( ) :
2010-03-30 14:46:26 +04:00
if re . match ( regex , fullname ) :
if reason is None :
return " "
return reason
return None
2010-09-13 22:53:54 +04:00
class FilterOps ( testtools . testresult . TestResult ) :
2010-03-30 14:46:26 +04:00
def control_msg ( self , msg ) :
pass # We regenerate control messages, so ignore this
2010-09-13 23:31:08 +04:00
def time ( self , time ) :
self . _ops . time ( time )
2010-03-30 14:46:26 +04:00
2010-03-30 16:36:25 +04:00
def progress ( self , delta , whence ) :
self . _ops . progress ( delta , whence )
2010-03-30 14:46:26 +04:00
def output_msg ( self , msg ) :
if self . output is None :
sys . stdout . write ( msg )
else :
self . output + = msg
2010-09-13 23:42:32 +04:00
def startTest ( self , test ) :
2010-09-14 00:09:46 +04:00
test = self . _add_prefix ( test )
2010-03-30 14:46:26 +04:00
if self . strip_ok_output :
self . output = " "
2010-09-13 23:42:32 +04:00
self . _ops . startTest ( test )
2010-03-30 14:46:26 +04:00
2010-09-14 00:09:46 +04:00
def _add_prefix ( self , test ) :
if self . prefix is not None :
return subunit . RemotedTestCase ( self . prefix + test . id ( ) )
else :
return test
2010-09-13 23:55:22 +04:00
def addError ( self , test , details = None ) :
2010-09-14 00:09:46 +04:00
test = self . _add_prefix ( test )
2010-09-14 00:29:38 +04:00
self . error_added + = 1
self . total_error + = 1
self . _ops . addError ( test , details )
self . output = None
2010-09-13 23:55:22 +04:00
2010-09-14 00:09:46 +04:00
def addSkip ( self , test , details = None ) :
test = self . _add_prefix ( test )
2010-09-14 00:29:38 +04:00
self . _ops . addSkip ( test , details )
self . output = None
2010-03-30 14:46:26 +04:00
2010-09-14 05:47:04 +04:00
def addExpectedFailure ( self , test , details = None ) :
2010-09-14 00:09:46 +04:00
test = self . _add_prefix ( test )
2010-09-14 05:47:04 +04:00
self . _ops . addExpectedFailure ( test , details )
2010-09-14 00:29:38 +04:00
self . output = None
2010-09-14 00:09:46 +04:00
def addFailure ( self , test , details = None ) :
test = self . _add_prefix ( test )
2010-09-14 00:29:38 +04:00
xfail_reason = find_in_list ( self . expected_failures , test . id ( ) )
if xfail_reason is not None :
2010-03-30 14:46:26 +04:00
self . xfail_added + = 1
self . total_xfail + = 1
2010-09-14 00:29:38 +04:00
if details is not None :
2010-09-14 05:47:04 +04:00
details = subunit . RemoteError ( details [ 1 ] . message + xfail_reason . decode ( " utf-8 " ) )
2010-09-14 00:29:38 +04:00
else :
2010-09-14 05:47:04 +04:00
details = subunit . RemoteError ( xfail_reason . decode ( " utf-8 " ) )
self . _ops . addExpectedFailure ( test , details )
2010-09-14 00:29:38 +04:00
else :
2010-03-30 14:46:26 +04:00
self . fail_added + = 1
self . total_fail + = 1
2010-09-14 00:29:38 +04:00
self . _ops . addFailure ( test , details )
if self . output :
self . _ops . output_msg ( self . output )
2010-03-30 14:46:26 +04:00
self . output = None
2010-09-14 00:29:38 +04:00
def addSuccess ( self , test , details = None ) :
test = self . _add_prefix ( test )
self . _ops . addSuccess ( test , details )
self . output = None
2010-03-30 14:46:26 +04:00
def skip_testsuite ( self , name , reason = None ) :
self . _ops . skip_testsuite ( name , reason )
def start_testsuite ( self , name ) :
self . _ops . start_testsuite ( name )
self . error_added = 0
self . fail_added = 0
self . xfail_added = 0
def end_testsuite ( self , name , result , reason = None ) :
xfail = False
if self . xfail_added > 0 :
xfail = True
if self . fail_added > 0 or self . error_added > 0 :
xfail = False
if xfail and result in ( " fail " , " failure " ) :
result = " xfail "
if self . fail_added > 0 and result != " failure " :
result = " failure "
if reason is None :
reason = " Subunit/Filter Reason "
reason + = " \n failures[ %d ] " % self . fail_added
if self . error_added > 0 and result != " error " :
result = " error "
if reason is None :
reason = " Subunit/Filter Reason "
reason + = " \n errors[ %d ] " % self . error_added
self . _ops . end_testsuite ( name , result , reason )
2010-09-13 23:17:05 +04:00
def __init__ ( self , out , prefix , expected_failures , strip_ok_output ) :
self . _ops = out
2010-03-30 14:46:26 +04:00
self . output = None
self . prefix = prefix
self . expected_failures = expected_failures
self . strip_ok_output = strip_ok_output
self . xfail_added = 0
self . total_xfail = 0
self . total_error = 0
self . total_fail = 0