Coverage for /Applications/PyCharm.app/Contents/plugins/python-ce/helpers/pycharm/teamcity/messages.py: 27%

1# coding=utf-8

2 import errno

3 from functools import wraps

4 import sys

5 import time

6

7

8 if sys.version_info < (3, ):

9 # Python 2

10 # flake8: noqa

11 text_type = unicode

12 else:

13 # Python 3

14 text_type = str

15

16# Capture some time functions to allow monkeypatching them in tests

17 _time = time.time

18 _localtime = time.localtime

19 _strftime = time.strftime

20

21 _quote = {"'": "|'", "|": "||", "\n": "|n", "\r": "|r", '[': '|[', ']': '|]'}

22

23

24 def escape_value(value):

25 return "".join(_quote.get(x, x) for x in value)

26

27

28 def retry_on_EAGAIN(callable):

29 # self.output seems to be non-blocking when running under teamcity.

30 @wraps(callable)

31 def wrapped(*args, **kwargs):

32 start_time = _time()

33 while True:

34 try:

35 return callable(*args, **kwargs)

36 except IOError as e:

37 if e.errno != errno.EAGAIN:

38 raise

39 # Give up after a minute.

40 if _time() - start_time > 60:

41 raise

42 time.sleep(.1)

43 return wrapped

44

45

46 class TeamcityServiceMessages(object):

47 def __init__(self, output=None, now=_time, encoding='auto'):

48 if output is None: 48 ↛ 50line 48 didn't jump to line 50 because the condition on line 48 was always true

49 output = sys.stdout

50 if sys.version_info < (3, ) or not hasattr(output, 'buffer'): 50 ↛ 51line 50 didn't jump to line 51 because the condition on line 50 was never true

51 self.output = output

52 else:

53 self.output = output.buffer

54 self.now = now

55

56 if encoding and encoding != 'auto': 56 ↛ 57line 56 didn't jump to line 57 because the condition on line 56 was never true

57 self.encoding = encoding

58 elif getattr(output, 'encoding', None) or encoding == 'auto': 58 ↛ 64line 58 didn't jump to line 64 because the condition on line 58 was always true

59 # Default encoding to 'utf-8' because it sucks if we fail with a

60 # `UnicodeEncodeError` simply because LANG didn't get propagated to

61 # a subprocess or something and sys.stdout.encoding is None

62 self.encoding = getattr(output, 'encoding', None) or 'utf-8'

63 else:

64 self.encoding = None

65

66 def encode(self, value):

67 if self.encoding and isinstance(value, text_type): 67 ↛ 69line 67 didn't jump to line 69 because the condition on line 67 was always true

68 value = value.encode(self.encoding)

69 return value

70

71 def decode(self, value):

72 if self.encoding and not isinstance(value, text_type): 72 ↛ 73line 72 didn't jump to line 73 because the condition on line 72 was never true

73 value = value.decode(self.encoding)

74 return value

75

76 if sys.version_info < (3, ):

77 def escapeValue(self, value):

78 return escape_value(self.encode(value))

79 else:

80 def escapeValue(self, value):

81 return escape_value(self.decode(value))

82

83 def message(self, messageName, **properties):

84 current_time = self.now()

85 (current_time_int, current_time_fraction) = divmod(current_time, 1)

86 current_time_struct = _localtime(current_time_int)

87

88 timestamp = _strftime("%Y-%m-%dT%H:%M:%S.", current_time_struct) + "%03d" % (int(current_time_fraction * 1000))

89 message = ("##teamcity[%s timestamp='%s'" % (messageName, timestamp))

90

91 for k in sorted(properties.keys()):

92 value = properties[k]

93 if value is None:

94 continue

95

96 message += (" %s='%s'" % (k, self.escapeValue(value)))

97

98 message += ("]\n")

99

100 # Python may buffer it for a long time, flushing helps to see real-time result

101 retry_on_EAGAIN(self.output.write)(self.encode(message))

102 retry_on_EAGAIN(self.output.flush)()

103

104 def _single_value_message(self, messageName, value):

105 message = ("##teamcity[%s '%s']\n" % (messageName, self.escapeValue(value)))

106

107 # Python may buffer it for a long time, flushing helps to see real-time result

108 retry_on_EAGAIN(self.output.write)(self.encode(message))

109 retry_on_EAGAIN(self.output.flush)()

110

111 def blockOpened(self, name, flowId=None):

112 self.message('blockOpened', name=name, flowId=flowId)

113

114 def blockClosed(self, name, flowId=None):

115 self.message('blockClosed', name=name, flowId=flowId)

116

117 # Special PyCharm-specific extension to track subtests, additional property is ignored by TeamCity

118 def subTestBlockOpened(self, name, subTestResult, flowId=None):

119 self.message('blockOpened', name=name, subTestResult=subTestResult, flowId=flowId)

120

121 def block(self, name, flowId=None):

122 import teamcity.context_managers as cm

123 return cm.block(self, name=name, flowId=flowId)

124

125 def compilationStarted(self, compiler):

126 self.message('compilationStarted', compiler=compiler)

127

128 def compilationFinished(self, compiler):

129 self.message('compilationFinished', compiler=compiler)

130

131 def compilation(self, compiler):

132 import teamcity.context_managers as cm

133 return cm.compilation(self, compiler=compiler)

134

135 def testSuiteStarted(self, suiteName, flowId=None):

136 self.message('testSuiteStarted', name=suiteName, flowId=flowId)

137

138 def testSuiteFinished(self, suiteName, flowId=None):

139 self.message('testSuiteFinished', name=suiteName, flowId=flowId)

140

141 def testSuite(self, name):

142 import teamcity.context_managers as cm

143 return cm.testSuite(self, name=name)

144

145 def testStarted(self, testName, captureStandardOutput=None, flowId=None, metainfo=None):

146 """

147

148 :param metainfo: Used to pass any payload from test runner to Intellij. See IDEA-176950

149 """

150 self.message('testStarted', name=testName, captureStandardOutput=captureStandardOutput, flowId=flowId, metainfo=metainfo)

151

152 def testFinished(self, testName, testDuration=None, flowId=None):

153 if testDuration is not None:

154 duration_ms = testDuration.days * 86400000 + \

155 testDuration.seconds * 1000 + \

156 int(testDuration.microseconds / 1000)

157 self.message('testFinished', name=testName, duration=str(duration_ms), flowId=flowId)

158 else:

159 self.message('testFinished', name=testName, flowId=flowId)

160

161 def test(self, testName, captureStandardOutput=None, testDuration=None, flowId=None):

162 import teamcity.context_managers as cm

163 return cm.test(self, testName=testName, captureStandardOutput=captureStandardOutput, testDuration=testDuration, flowId=flowId)

164

165 # Unsupported in TeamCity, used in IntellIJ-based IDEs to predict number of tests to be run in the test session

166 def testCount(self, count, flowId=None):

167 self.message('testCount', count=str(count), flowId=flowId)

168

169 def testIgnored(self, testName, message='', flowId=None):

170 self.message('testIgnored', name=testName, message=message, flowId=flowId)

171

172 def testFailed(self, testName, message='', details='', flowId=None, comparison_failure=None):

173 if not comparison_failure: 173 ↛ 176line 173 didn't jump to line 176 because the condition on line 173 was always true

174 self.message('testFailed', name=testName, message=message, details=details, flowId=flowId)

175 else:

176 diff_message = u"\n{0} != {1}\n".format(comparison_failure.actual, comparison_failure.expected)

177 self.message('testFailed',

178 name=testName,

179 message=text_type(message) + text_type(diff_message),

180 details=details,

181 flowId=flowId,

182 type="comparisonFailure",

183 actual=comparison_failure.actual,

184 expected=comparison_failure.expected)

185

186 def testStdOut(self, testName, out, flowId=None):

187 self.message('testStdOut', name=testName, out=out, flowId=flowId)

188

189 def testStdErr(self, testName, out, flowId=None):

190 self.message('testStdErr', name=testName, out=out, flowId=flowId)

191

192 def publishArtifacts(self, path, flowId=None):

193 self._single_value_message('publishArtifacts', path)

194

195 def progressMessage(self, message):

196 self._single_value_message('progressMessage', message)

197

198 def progressStart(self, message):

199 self._single_value_message('progressStart', message)

200

201 def progressFinish(self, message):

202 self._single_value_message('progressFinish', message)

203

204 def progress(self, message):

205 import teamcity.context_managers as cm

206 return cm.progress(self, message=message)

207

208 def buildProblem(self, description, identity):

209 self.message('buildProblem', description=description, identity=identity)

210

211 def buildStatus(self, status, text):

212 self.message('buildStatus', status=status, text=text)

213

214 def setParameter(self, name, value):

215 self.message('setParameter', name=name, value=value)

216

217 def buildStatisticLinesCovered(self, linesCovered):

218 self.message('buildStatisticValue', key='CodeCoverageAbsLCovered', value=str(linesCovered))

219

220 def buildStatisticTotalLines(self, totalLines):

221 self.message('buildStatisticValue', key='CodeCoverageAbsLTotal', value=str(totalLines))

222

223 def buildStatisticLinesUncovered(self, linesUncovered):

224 self.message('buildStatisticValue', key='CodeCoverageAbsLUncovered', value=str(linesUncovered))

225

226 def enableServiceMessages(self, flowId=None):

227 self.message('enableServiceMessages', flowId=flowId)

228

229 def disableServiceMessages(self, flowId=None):

230 self.message('disableServiceMessages', flowId=flowId)

231

232 def serviceMessagesDisabled(self, flowId=None):

233 import teamcity.context_managers as cm

234 return cm.serviceMessagesDisabled(self, flowId=flowId)

235

236 def serviceMessagesEnabled(self, flowId=None):

237 import teamcity.context_managers as cm

238 return cm.serviceMessagesEnabled(self, flowId=flowId)

239

240 def importData(self, typeID, pathToXMLFile):

241 self.message('importData', type=typeID, path=pathToXMLFile)

242

243 def customMessage(self, text, status, errorDetails='', flowId=None):

244 self.message('message', text=text, status=status, errorDetails=errorDetails, flowId=flowId)