# Python Tools for Visual Studio # Copyright(c) Microsoft Corporation # All rights reserved. # # Licensed under the Apache License, Version 2.0 (the License); you may not use # this file except in compliance with the License. You may obtain a copy of the # License at http://www.apache.org/licenses/LICENSE-2.0 # # THIS CODE IS PROVIDED ON AN *AS IS* BASIS, WITHOUT WARRANTIES OR CONDITIONS # OF ANY KIND, EITHER EXPRESS OR IMPLIED, INCLUDING WITHOUT LIMITATION ANY # IMPLIED WARRANTIES OR CONDITIONS OF TITLE, FITNESS FOR A PARTICULAR PURPOSE, # MERCHANTABLITY OR NON-INFRINGEMENT. # # See the Apache Version 2.0 License for specific language governing # permissions and limitations under the License. __author__ = "Microsoft Corporation " __version__ = "3.0.0.0" import json import os import signal import socket import sys import traceback import unittest try: import thread except: import _thread as thread class _TestOutput(object): """file like object which redirects output to the repl window.""" errors = "strict" def __init__(self, old_out, is_stdout): self.is_stdout = is_stdout self.old_out = old_out if sys.version >= "3." and hasattr(old_out, "buffer"): self.buffer = _TestOutputBuffer(old_out.buffer, is_stdout) def flush(self): if self.old_out: self.old_out.flush() def writelines(self, lines): for line in lines: self.write(line) @property def encoding(self): return "utf8" def write(self, value): _channel.send_event("stdout" if self.is_stdout else "stderr", content=value) if self.old_out: self.old_out.write(value) # flush immediately, else things go wonky and out of order self.flush() def isatty(self): return True def next(self): pass @property def name(self): if self.is_stdout: return "" else: return "" def __getattr__(self, name): return getattr(self.old_out, name) class _TestOutputBuffer(object): def __init__(self, old_buffer, is_stdout): self.buffer = old_buffer self.is_stdout = is_stdout def write(self, data): _channel.send_event("stdout" if self.is_stdout else "stderr", content=data) self.buffer.write(data) def flush(self): self.buffer.flush() def truncate(self, pos=None): return self.buffer.truncate(pos) def tell(self): return self.buffer.tell() def seek(self, pos, whence=0): return self.buffer.seek(pos, whence) class _IpcChannel(object): def __init__(self, socket, callback): self.socket = socket self.seq = 0 self.callback = callback self.lock = thread.allocate_lock() self._closed = False # start the testing reader thread loop self.test_thread_id = thread.start_new_thread(self.readSocket, ()) def close(self): self._closed = True def readSocket(self): try: data = self.socket.recv(1024) self.callback() except OSError: if not self._closed: raise def receive(self): pass def send_event(self, name, **args): with self.lock: body = {"type": "event", "seq": self.seq, "event": name, "body": args} self.seq += 1 content = json.dumps(body).encode("utf8") headers = ("Content-Length: %d\n\n" % (len(content),)).encode("utf8") self.socket.send(headers) self.socket.send(content) _channel = None class VsTestResult(unittest.TextTestResult): def startTest(self, test): super(VsTestResult, self).startTest(test) if _channel is not None: _channel.send_event(name="start", test=test.id()) def addError(self, test, err): super(VsTestResult, self).addError(test, err) self.sendResult(test, "error", err) def addFailure(self, test, err): super(VsTestResult, self).addFailure(test, err) self.sendResult(test, "failed", err) def addSuccess(self, test): super(VsTestResult, self).addSuccess(test) self.sendResult(test, "passed") def addSkip(self, test, reason): super(VsTestResult, self).addSkip(test, reason) self.sendResult(test, "skipped") def addExpectedFailure(self, test, err): super(VsTestResult, self).addExpectedFailure(test, err) self.sendResult(test, "failed-expected", err) def addUnexpectedSuccess(self, test): super(VsTestResult, self).addUnexpectedSuccess(test) self.sendResult(test, "passed-unexpected") def addSubTest(self, test, subtest, err): super(VsTestResult, self).addSubTest(test, subtest, err) self.sendResult( test, "subtest-passed" if err is None else "subtest-failed", err, subtest ) def sendResult(self, test, outcome, trace=None, subtest=None): if _channel is not None: tb = None message = None if trace is not None: traceback.print_exc() formatted = traceback.format_exception(*trace) # Remove the 'Traceback (most recent call last)' formatted = formatted[1:] tb = "".join(formatted) message = str(trace[1]) result = { "outcome": outcome, "traceback": tb, "message": message, "test": test.id(), } if subtest is not None: result["subtest"] = subtest.id() _channel.send_event("result", **result) def stopTests(): try: os.kill(os.getpid(), signal.SIGUSR1) except: try: os.kill(os.getpid(), signal.SIGTERM) except: pass class ExitCommand(Exception): pass def signal_handler(signal, frame): raise ExitCommand() def main(): import os import sys import unittest from optparse import OptionParser global _channel parser = OptionParser( prog="visualstudio_py_testlauncher", usage="Usage: %prog [