1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

24

25

26

27

28

29

30

31

32

33

34

35

36

37

38

39

40

41

42

43

44

45

46

47

48

49

50

51

52

53

54

55

56

57

58

59

60

61

62

63

64

65

66

67

68

69

70

71

72

73

74

75

76

77

78

79

80

81

82

83

84

85

86

87

88

89

90

91

92

93

94

95

96

97

98

99

100

101

102

103

104

105

106

107

108

109

110

111

112

113

114

115

116

117

118

119

120

121

122

123

124

125

126

127

128

129

130

131

132

133

134

135

136

137

138

139

140

141

142

143

144

145

146

147

148

149

150

151

152

153

154

155

156

157

158

159

160

161

162

163

164

165

166

167

168

169

170

171

172

173

174

175

176

177

178

179

180

181

182

183

184

185

186

187

188

189

190

191

192

193

194

195

196

197

198

199

200

""" 

Test Result 

----------- 

 

Provides a TextTestResult that extends unittest's _TextTestResult to 

provide support for error classes (such as the builtin skip and 

deprecated classes), and hooks for plugins to take over or extend 

reporting. 

""" 

 

import logging 

try: 

# 2.7+ 

from unittest.runner import _TextTestResult 

except ImportError: 

from unittest import _TextTestResult 

from nose.config import Config 

from nose.util import isclass, ln as _ln # backwards compat 

 

log = logging.getLogger('nose.result') 

 

 

def _exception_detail(exc): 

# this is what stdlib module traceback does 

try: 

return str(exc) 

except: 

return '<unprintable %s object>' % type(exc).__name__ 

 

 

class TextTestResult(_TextTestResult): 

"""Text test result that extends unittest's default test result 

support for a configurable set of errorClasses (eg, Skip, 

Deprecated, TODO) that extend the errors/failures/success triad. 

""" 

def __init__(self, stream, descriptions, verbosity, config=None, 

errorClasses=None): 

if errorClasses is None: 

errorClasses = {} 

self.errorClasses = errorClasses 

if config is None: 

config = Config() 

self.config = config 

_TextTestResult.__init__(self, stream, descriptions, verbosity) 

 

def addSkip(self, test, reason): 

# 2.7 skip compat 

from nose.plugins.skip import SkipTest 

if SkipTest in self.errorClasses: 

storage, label, isfail = self.errorClasses[SkipTest] 

storage.append((test, reason)) 

self.printLabel(label, (SkipTest, reason, None)) 

 

def addError(self, test, err): 

"""Overrides normal addError to add support for 

errorClasses. If the exception is a registered class, the 

error will be added to the list for that class, not errors. 

""" 

ec, ev, tb = err 

try: 

exc_info = self._exc_info_to_string(err, test) 

except TypeError: 

# 2.3 compat 

exc_info = self._exc_info_to_string(err) 

for cls, (storage, label, isfail) in list(self.errorClasses.items()): 

#if 'Skip' in cls.__name__ or 'Skip' in ec.__name__: 

# from nose.tools import set_trace 

# set_trace() 

if isclass(ec) and issubclass(ec, cls): 

if isfail: 

test.passed = False 

storage.append((test, exc_info)) 

self.printLabel(label, err) 

return 

self.errors.append((test, exc_info)) 

test.passed = False 

self.printLabel('ERROR') 

 

# override to bypass changes in 2.7 

def getDescription(self, test): 

if self.descriptions: 

return test.shortDescription() or str(test) 

else: 

return str(test) 

 

def printLabel(self, label, err=None): 

# Might get patched into a streamless result 

stream = getattr(self, 'stream', None) 

if stream is not None: 

if self.showAll: 

message = [label] 

if err: 

detail = _exception_detail(err[1]) 

if detail: 

message.append(detail) 

stream.writeln(": ".join(message)) 

elif self.dots: 

stream.write(label[:1]) 

 

def printErrors(self): 

"""Overrides to print all errorClasses errors as well. 

""" 

_TextTestResult.printErrors(self) 

for cls in list(self.errorClasses.keys()): 

storage, label, isfail = self.errorClasses[cls] 

if isfail: 

self.printErrorList(label, storage) 

# Might get patched into a result with no config 

if hasattr(self, 'config'): 

self.config.plugins.report(self.stream) 

 

def printSummary(self, start, stop): 

"""Called by the test runner to print the final summary of test 

run results. 

""" 

write = self.stream.write 

writeln = self.stream.writeln 

taken = float(stop - start) 

run = self.testsRun 

plural = run != 1 and "s" or "" 

 

writeln(self.separator2) 

writeln("Ran %s test%s in %.3fs" % (run, plural, taken)) 

writeln() 

 

summary = {} 

eckeys = list(self.errorClasses.keys()) 

for cls in eckeys: 

storage, label, isfail = self.errorClasses[cls] 

count = len(storage) 

if not count: 

continue 

summary[label] = count 

if len(self.failures): 

summary['failures'] = len(self.failures) 

if len(self.errors): 

summary['errors'] = len(self.errors) 

 

if not self.wasSuccessful(): 

write("FAILED") 

else: 

write("OK") 

items = list(summary.items()) 

if items: 

items.sort() 

write(" (") 

write(", ".join(["%s=%s" % (label, count) for 

label, count in items])) 

writeln(")") 

else: 

writeln() 

 

def wasSuccessful(self): 

"""Overrides to check that there are no errors in errorClasses 

lists that are marked as errors and should cause a run to 

fail. 

""" 

if self.errors or self.failures: 

return False 

for cls in list(self.errorClasses.keys()): 

storage, label, isfail = self.errorClasses[cls] 

if not isfail: 

continue 

if storage: 

return False 

return True 

 

def _addError(self, test, err): 

try: 

exc_info = self._exc_info_to_string(err, test) 

except TypeError: 

# 2.3: does not take test arg 

exc_info = self._exc_info_to_string(err) 

self.errors.append((test, exc_info)) 

if self.showAll: 

self.stream.write('ERROR') 

elif self.dots: 

self.stream.write('E') 

 

def _exc_info_to_string(self, err, test=None): 

# 2.7 skip compat 

from nose.plugins.skip import SkipTest 

if isclass(err[0]) and issubclass(err[0], SkipTest): 

return str(err[1]) 

# 2.3/2.4 -- 2.4 passes test, 2.3 does not 

try: 

return _TextTestResult._exc_info_to_string(self, err, test) 

except TypeError: 

# 2.3: does not take test arg 

return _TextTestResult._exc_info_to_string(self, err) 

 

 

def ln(*arg, **kw): 

from warnings import warn 

warn("ln() has moved to nose.util from nose.result and will be removed " 

"from nose.result in a future release. Please update your imports ", 

DeprecationWarning) 

return _ln(*arg, **kw)