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

154 statements  

« prev     ^ index     » next       coverage.py v7.2.7, created at 2023-06-12 16:26 -0700

1# coding=utf-8 

2import errno 

3from functools import wraps 

4import sys 

5import time 

6 

7 

8if sys.version_info < (3, ): 

9 # Python 2 

10 # flake8: noqa 

11 text_type = unicode 

12else: 

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 

24def escape_value(value): 

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

26 

27 

28def 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 

46class TeamcityServiceMessages(object): 

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

48 if output is None: 

49 output = sys.stdout 

50 if sys.version_info < (3, ) or not hasattr(output, 'buffer'): 

51 self.output = output 

52 else: 

53 self.output = output.buffer 

54 self.now = now 

55 

56 if encoding and encoding != 'auto': 

57 self.encoding = encoding 

58 elif getattr(output, 'encoding', None) or encoding == 'auto': 

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): 

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): 

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: 

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)