PY-9727: Initial commit for tox configuration

* Tox configuration created
* Tox helper added
* Refactoting to support several runners
This commit is contained in:
Ilya.Kazakevich
2015-12-07 16:37:35 +03:00
parent e001e7dc9d
commit a7ac25ffa1
21 changed files with 487 additions and 16 deletions
+68
View File
@@ -0,0 +1,68 @@
# coding=utf-8
"""
Runs tox from current directory.
It supports any runner, but well-known runners (py.test and unittest) are switched to our internal runners to provide better support
"""
import sys
from tox import config as tox_config, session as tox_session
from tox.session import Reporter
from tcmessages import TeamcityServiceMessages
import os
helpers_dir = str(os.path.split(__file__)[0])
# List of local runners to use
_RUNNERS = {"unit2": [os.path.join(helpers_dir, "utrunner.py"), os.getcwd(), "true"]}
teamcity = TeamcityServiceMessages()
class _Reporter(Reporter):
def logaction_start(self, action):
super(_Reporter, self).logaction_start(action)
if action.activity == "getenv":
teamcity.testSuiteStarted(action.id)
self.current_suite = action.id
def logaction_finish(self, action):
super(_Reporter, self).logaction_finish(action)
if action.activity == "runtests":
teamcity.testSuiteFinished(action.id)
def error(self, msg):
super(_Reporter, self).error(msg)
name = teamcity.current_test_name()
if name:
teamcity.testError(name, msg)
if name == teamcity.topmost_suite:
teamcity.testSuiteFinished(name)
else:
sys.stderr.write(msg)
def skip(self, msg):
super(_Reporter, self).skip(msg)
name = teamcity.current_test_name()
if name:
teamcity.testFinished(name)
config = tox_config.parseconfig()
for env, tmp_config in config.envconfigs.items():
if not tmp_config.setenv:
tmp_config.setenv = dict()
tmp_config.setenv.update({"_jb_do_not_call_enter_matrix": "1"})
commands = tmp_config.commands
if isinstance(commands, list) and len(commands) == 1:
command_with_arguments = commands[0]
if command_with_arguments[0] in _RUNNERS:
command_with_arguments = _RUNNERS[command_with_arguments[0]]
tmp_config.commands = [command_with_arguments]
session = tox_session.Session(config, Report=_Reporter)
teamcity.testMatrixEntered()
session.runcommand()
+29 -1
View File
@@ -7,6 +7,14 @@ class TeamcityServiceMessages:
def __init__(self, output=sys.stdout, prepend_linebreak=False):
self.output = output
self.prepend_linebreak = prepend_linebreak
self.test_stack = []
"""
Names of tests
"""
self.topmost_suite = None
"""
Last suite we entered in
"""
def escapeValue(self, value):
if sys.version_info[0] <= 2 and isinstance(value, unicode):
@@ -28,21 +36,27 @@ class TeamcityServiceMessages:
def testSuiteStarted(self, suiteName, location=None):
self.message('testSuiteStarted', name=suiteName, locationHint=location)
self.test_stack.append(suiteName)
self.topmost_suite = suiteName
def testSuiteFinished(self, suiteName):
self.message('testSuiteFinished', name=suiteName)
self.__pop_current_test()
def testStarted(self, testName, location=None):
self.message('testStarted', name=testName, locationHint=location)
self.test_stack.append(testName)
def testFinished(self, testName, duration=None):
self.message('testFinished', name=testName, duration=duration)
self.__pop_current_test()
def testIgnored(self, testName, message=''):
self.message('testIgnored', name=testName, message=message)
self.testFinished(testName)
def testFailed(self, testName, message='', details='', expected='', actual='', duration=None):
"""
Marks test as failed. *CAUTION*: This method calls ``testFinished``, so you do not need
@@ -56,10 +70,24 @@ class TeamcityServiceMessages:
self.message('testFailed', name=testName, message=message, details=details)
self.testFinished(testName, int(duration) if duration else None)
def __pop_current_test(self):
try:
self.test_stack.pop()
except IndexError:
pass
def testError(self, testName, message='', details='', duration=None):
self.message('testFailed', name=testName, message=message, details=details, error="true")
self.testFinished(testName, int(duration) if duration else None)
def current_test_name(self):
"""
:return: name of current test we are in
"""
return self.test_stack[-1] if len(self.test_stack) > 0 else None
def testStdOut(self, testName, out):
self.message('testStdOut', name=testName, out=out)
+7 -1
View File
@@ -1,3 +1,4 @@
import os
import traceback, sys
from unittest import TestResult
import datetime
@@ -37,13 +38,18 @@ def smart_str(s):
class TeamcityTestResult(TestResult):
"""
Set ``_jb_do_not_call_enter_matrix`` to prevent it from runnig "enter matrix"
"""
def __init__(self, stream=sys.stdout, *args, **kwargs):
TestResult.__init__(self)
for arg, value in kwargs.items():
setattr(self, arg, value)
self.output = stream
self.messages = TeamcityServiceMessages(self.output, prepend_linebreak=True)
self.messages.testMatrixEntered()
if not "_jb_do_not_call_enter_matrix" in os.environ:
self.messages.testMatrixEntered()
self.current_failed = False
self.current_suite = None
self.subtest_suite = None
+13 -13
View File
@@ -38,6 +38,8 @@ def loadSource(fileName):
cnt += 1
moduleName = getModuleName(prefix, cnt)
debug("/ Loading " + fileName + " as " + moduleName)
if os.path.isdir(fileName):
fileName = fileName + os.path.sep
module = imp.load_source(moduleName, fileName)
modules[moduleName] = module
return module
@@ -56,17 +58,15 @@ def walkModules(modulesAndPattern, dirname, names):
# For default pattern see https://docs.python.org/2/library/unittest.html#test-discovery
def loadModulesFromFolderRec(folder, pattern="test*.py"):
modules = []
if PYTHON_VERSION_MAJOR == 3:
# fnmatch converts glob to regexp
prog_list = [re.compile(fnmatch.translate(pat.strip())) for pat in pattern.split(',')]
for root, dirs, files in os.walk(folder):
for name in files:
for prog in prog_list:
if name.endswith(".py") and prog.match(name):
modules.append(loadSource(os.path.join(root, name)))
else: # actually for jython compatibility
os.path.walk(folder, walkModules, (modules, pattern))
# fnmatch converts glob to regexp
prog_list = [re.compile(fnmatch.translate(pat.strip())) for pat in pattern.split(',')]
for root, dirs, files in os.walk(folder):
files = [f for f in files if not f[0] == '.']
dirs[:] = [d for d in dirs if not d[0] == '.']
for name in files:
for prog in prog_list:
if name.endswith(".py") and prog.match(name):
modules.append(loadSource(os.path.join(root, name)))
return modules
testLoader = TestLoader()
@@ -109,11 +109,11 @@ if __name__ == "__main__":
a_splitted = a[0].split("_args_separator_") # ";" can't be used with bash, so we use "_args_separator_"
if len(a_splitted) != 1:
# means we have pattern to match against
if a_splitted[0].endswith(os.path.sep):
if os.path.isdir(a_splitted[0]):
debug("/ from folder " + a_splitted[0] + ". Use pattern: " + a_splitted[1])
modules = loadModulesFromFolderRec(a_splitted[0], a_splitted[1])
else:
if a[0].endswith(os.path.sep):
if os.path.isdir(a[0]):
debug("/ from folder " + a[0])
modules = loadModulesFromFolderRec(a[0])
else: