My dotfiles
Du kannst nicht mehr als 25 Themen auswählen Themen müssen entweder mit einem Buchstaben oder einer Ziffer beginnen. Sie können Bindestriche („-“) enthalten und bis zu 35 Zeichen lang sein.
 
 

348 Zeilen
12 KiB

  1. # Python Tools for Visual Studio
  2. # Copyright(c) Microsoft Corporation
  3. # All rights reserved.
  4. #
  5. # Licensed under the Apache License, Version 2.0 (the License); you may not use
  6. # this file except in compliance with the License. You may obtain a copy of the
  7. # License at http://www.apache.org/licenses/LICENSE-2.0
  8. #
  9. # THIS CODE IS PROVIDED ON AN *AS IS* BASIS, WITHOUT WARRANTIES OR CONDITIONS
  10. # OF ANY KIND, EITHER EXPRESS OR IMPLIED, INCLUDING WITHOUT LIMITATION ANY
  11. # IMPLIED WARRANTIES OR CONDITIONS OF TITLE, FITNESS FOR A PARTICULAR PURPOSE,
  12. # MERCHANTABLITY OR NON-INFRINGEMENT.
  13. #
  14. # See the Apache Version 2.0 License for specific language governing
  15. # permissions and limitations under the License.
  16. __author__ = "Microsoft Corporation <ptvshelp@microsoft.com>"
  17. __version__ = "3.0.0.0"
  18. import os
  19. import sys
  20. import json
  21. import unittest
  22. import socket
  23. import traceback
  24. from types import CodeType, FunctionType
  25. import signal
  26. try:
  27. import thread
  28. except:
  29. import _thread as thread
  30. class _TestOutput(object):
  31. """file like object which redirects output to the repl window."""
  32. errors = 'strict'
  33. def __init__(self, old_out, is_stdout):
  34. self.is_stdout = is_stdout
  35. self.old_out = old_out
  36. if sys.version >= '3.' and hasattr(old_out, 'buffer'):
  37. self.buffer = _TestOutputBuffer(old_out.buffer, is_stdout)
  38. def flush(self):
  39. if self.old_out:
  40. self.old_out.flush()
  41. def writelines(self, lines):
  42. for line in lines:
  43. self.write(line)
  44. @property
  45. def encoding(self):
  46. return 'utf8'
  47. def write(self, value):
  48. _channel.send_event('stdout' if self.is_stdout else 'stderr', content=value)
  49. if self.old_out:
  50. self.old_out.write(value)
  51. # flush immediately, else things go wonky and out of order
  52. self.flush()
  53. def isatty(self):
  54. return True
  55. def next(self):
  56. pass
  57. @property
  58. def name(self):
  59. if self.is_stdout:
  60. return "<stdout>"
  61. else:
  62. return "<stderr>"
  63. def __getattr__(self, name):
  64. return getattr(self.old_out, name)
  65. class _TestOutputBuffer(object):
  66. def __init__(self, old_buffer, is_stdout):
  67. self.buffer = old_buffer
  68. self.is_stdout = is_stdout
  69. def write(self, data):
  70. _channel.send_event('stdout' if self.is_stdout else 'stderr', content=data)
  71. self.buffer.write(data)
  72. def flush(self):
  73. self.buffer.flush()
  74. def truncate(self, pos = None):
  75. return self.buffer.truncate(pos)
  76. def tell(self):
  77. return self.buffer.tell()
  78. def seek(self, pos, whence = 0):
  79. return self.buffer.seek(pos, whence)
  80. class _IpcChannel(object):
  81. def __init__(self, socket, callback):
  82. self.socket = socket
  83. self.seq = 0
  84. self.callback = callback
  85. self.lock = thread.allocate_lock()
  86. self._closed = False
  87. # start the testing reader thread loop
  88. self.test_thread_id = thread.start_new_thread(self.readSocket, ())
  89. def close(self):
  90. self._closed = True
  91. def readSocket(self):
  92. try:
  93. data = self.socket.recv(1024)
  94. self.callback()
  95. except OSError:
  96. if not self._closed:
  97. raise
  98. def receive(self):
  99. pass
  100. def send_event(self, name, **args):
  101. with self.lock:
  102. body = {'type': 'event', 'seq': self.seq, 'event':name, 'body':args}
  103. self.seq += 1
  104. content = json.dumps(body).encode('utf8')
  105. headers = ('Content-Length: %d\n\n' % (len(content), )).encode('utf8')
  106. self.socket.send(headers)
  107. self.socket.send(content)
  108. _channel = None
  109. class VsTestResult(unittest.TextTestResult):
  110. def startTest(self, test):
  111. super(VsTestResult, self).startTest(test)
  112. if _channel is not None:
  113. _channel.send_event(
  114. name='start',
  115. test = test.id()
  116. )
  117. def addError(self, test, err):
  118. super(VsTestResult, self).addError(test, err)
  119. self.sendResult(test, 'error', err)
  120. def addFailure(self, test, err):
  121. super(VsTestResult, self).addFailure(test, err)
  122. self.sendResult(test, 'failed', err)
  123. def addSuccess(self, test):
  124. super(VsTestResult, self).addSuccess(test)
  125. self.sendResult(test, 'passed')
  126. def addSkip(self, test, reason):
  127. super(VsTestResult, self).addSkip(test, reason)
  128. self.sendResult(test, 'skipped')
  129. def addExpectedFailure(self, test, err):
  130. super(VsTestResult, self).addExpectedFailure(test, err)
  131. self.sendResult(test, 'failed', err)
  132. def addUnexpectedSuccess(self, test):
  133. super(VsTestResult, self).addUnexpectedSuccess(test)
  134. self.sendResult(test, 'passed')
  135. def sendResult(self, test, outcome, trace = None):
  136. if _channel is not None:
  137. tb = None
  138. message = None
  139. if trace is not None:
  140. traceback.print_exc()
  141. formatted = traceback.format_exception(*trace)
  142. # Remove the 'Traceback (most recent call last)'
  143. formatted = formatted[1:]
  144. tb = ''.join(formatted)
  145. message = str(trace[1])
  146. _channel.send_event(
  147. name='result',
  148. outcome=outcome,
  149. traceback = tb,
  150. message = message,
  151. test = test.id()
  152. )
  153. def stopTests():
  154. try:
  155. os.kill(os.getpid(), signal.SIGUSR1)
  156. except:
  157. try:
  158. os.kill(os.getpid(), signal.SIGTERM)
  159. except:
  160. pass
  161. class ExitCommand(Exception):
  162. pass
  163. def signal_handler(signal, frame):
  164. raise ExitCommand()
  165. def main():
  166. import os
  167. import sys
  168. import unittest
  169. from optparse import OptionParser
  170. global _channel
  171. parser = OptionParser(prog = 'visualstudio_py_testlauncher', usage = 'Usage: %prog [<option>] <test names>... ')
  172. parser.add_option('--debug', action='store_true', help='Whether debugging the unit tests')
  173. parser.add_option('-x', '--mixed-mode', action='store_true', help='wait for mixed-mode debugger to attach')
  174. parser.add_option('-t', '--test', type='str', dest='tests', action='append', help='specifies a test to run')
  175. parser.add_option('--testFile', type='str', help='Fully qualitified path to file name')
  176. parser.add_option('-c', '--coverage', type='str', help='enable code coverage and specify filename')
  177. parser.add_option('-r', '--result-port', type='int', help='connect to port on localhost and send test results')
  178. parser.add_option('--us', type='str', help='Directory to start discovery')
  179. parser.add_option('--up', type='str', help='Pattern to match test files (''test*.py'' default)')
  180. parser.add_option('--ut', type='str', help='Top level directory of project (default to start directory)')
  181. parser.add_option('--uvInt', '--verboseInt', type='int', help='Verbose output (0 none, 1 (no -v) simple, 2 (-v) full)')
  182. parser.add_option('--uf', '--failfast', type='str', help='Stop on first failure')
  183. parser.add_option('--uc', '--catch', type='str', help='Catch control-C and display results')
  184. (opts, _) = parser.parse_args()
  185. if opts.debug:
  186. from ptvsd.visualstudio_py_debugger import DONT_DEBUG, DEBUG_ENTRYPOINTS, get_code
  187. sys.path[0] = os.getcwd()
  188. if opts.result_port:
  189. try:
  190. signal.signal(signal.SIGUSR1, signal_handler)
  191. except:
  192. try:
  193. signal.signal(signal.SIGTERM, signal_handler)
  194. except:
  195. pass
  196. _channel = _IpcChannel(socket.create_connection(('127.0.0.1', opts.result_port)), stopTests)
  197. sys.stdout = _TestOutput(sys.stdout, is_stdout = True)
  198. sys.stderr = _TestOutput(sys.stderr, is_stdout = False)
  199. if opts.debug:
  200. DONT_DEBUG.append(os.path.normcase(__file__))
  201. DEBUG_ENTRYPOINTS.add(get_code(main))
  202. pass
  203. elif opts.mixed_mode:
  204. # For mixed-mode attach, there's no ptvsd and hence no wait_for_attach(),
  205. # so we have to use Win32 API in a loop to do the same thing.
  206. from time import sleep
  207. from ctypes import windll, c_char
  208. while True:
  209. if windll.kernel32.IsDebuggerPresent() != 0:
  210. break
  211. sleep(0.1)
  212. try:
  213. debugger_helper = windll['Microsoft.PythonTools.Debugger.Helper.x86.dll']
  214. except WindowsError:
  215. debugger_helper = windll['Microsoft.PythonTools.Debugger.Helper.x64.dll']
  216. isTracing = c_char.in_dll(debugger_helper, "isTracing")
  217. while True:
  218. if isTracing.value != 0:
  219. break
  220. sleep(0.1)
  221. cov = None
  222. try:
  223. if opts.coverage:
  224. try:
  225. import coverage
  226. cov = coverage.coverage(opts.coverage)
  227. cov.load()
  228. cov.start()
  229. except:
  230. pass
  231. if opts.tests is None and opts.testFile is None:
  232. if opts.us is None:
  233. opts.us = '.'
  234. if opts.up is None:
  235. opts.up = 'test*.py'
  236. tests = unittest.defaultTestLoader.discover(opts.us, opts.up)
  237. else:
  238. # loadTestsFromNames doesn't work well (with duplicate file names or class names)
  239. # Easier approach is find the test suite and use that for running
  240. loader = unittest.TestLoader()
  241. # opts.us will be passed in
  242. suites = loader.discover(opts.us, pattern=os.path.basename(opts.testFile))
  243. suite = None
  244. tests = None
  245. if opts.tests is None:
  246. # Run everything in the test file
  247. tests = suites
  248. else:
  249. # Run a specific test class or test method
  250. for test_suite in suites._tests:
  251. for cls in test_suite._tests:
  252. try:
  253. for m in cls._tests:
  254. testId = m.id()
  255. if testId.startswith(opts.tests[0]):
  256. suite = cls
  257. if testId == opts.tests[0]:
  258. tests = unittest.TestSuite([m])
  259. break
  260. except Exception as err:
  261. errorMessage = traceback.format_exception()
  262. pass
  263. if tests is None:
  264. tests = suite
  265. if tests is None and suite is None:
  266. _channel.send_event(
  267. name='error',
  268. outcome='',
  269. traceback = '',
  270. message = 'Failed to identify the test',
  271. test = ''
  272. )
  273. if opts.uvInt is None:
  274. opts.uvInt = 0
  275. if opts.uf is not None:
  276. runner = unittest.TextTestRunner(verbosity=opts.uvInt, resultclass=VsTestResult, failfast=True)
  277. else:
  278. runner = unittest.TextTestRunner(verbosity=opts.uvInt, resultclass=VsTestResult)
  279. result = runner.run(tests)
  280. if _channel is not None:
  281. _channel.close()
  282. sys.exit(not result.wasSuccessful())
  283. finally:
  284. if cov is not None:
  285. cov.stop()
  286. cov.save()
  287. cov.xml_report(outfile = opts.coverage + '.xml', omit=__file__)
  288. if _channel is not None:
  289. _channel.send_event(
  290. name='done'
  291. )
  292. _channel.socket.close()
  293. # prevent generation of the error 'Error in sys.exitfunc:'
  294. try:
  295. sys.stdout.close()
  296. except:
  297. pass
  298. try:
  299. sys.stderr.close()
  300. except:
  301. pass
  302. if __name__ == '__main__':
  303. main()