1import os
2import sys
3helpers_dir = os.getenv("PYCHARM_HELPERS_DIR", sys.path[0])
4if sys.path[0] != helpers_dir:
5    sys.path.insert(0, helpers_dir)
6
7from tcmessages import TeamcityServiceMessages
8from pycharm_run_utils import adjust_sys_path
9
10adjust_sys_path(False)
11
12# Directory where test script exist
13CURRENT_DIR_NAME = ""
14if sys.argv:
15  last_arg = sys.argv[-1]
16
17  if os.path.isfile(last_arg):
18    CURRENT_DIR_NAME = os.path.dirname(last_arg)
19  else:
20    CURRENT_DIR_NAME = last_arg
21    if not str(last_arg).endswith(os.sep):
22      CURRENT_DIR_NAME = last_arg + os.sep
23
24messages = TeamcityServiceMessages(prepend_linebreak=True)
25if not "_jb_do_not_call_enter_matrix" in os.environ:
26  messages.testMatrixEntered()
27try:
28  import pytest
29  PYVERSION = [int(x) for x in pytest.__version__.split(".")]
30except:
31  import py
32  PYVERSION = [int(x) for x in py.__version__.split(".")]
33
34def get_name(nodeid):
35  return nodeid.split("::")[-1]
36
37def fspath_to_url(fspath):
38  return "file:///" + str(fspath).replace("\\", "/")
39
40if PYVERSION > [1, 4, 0]:
41  items = {}
42  current_suite = None
43  current_file = None
44  current_file_suite = None
45
46  def pytest_collection_finish(session):
47    messages.testCount(len(session.items))
48
49  def pytest_runtest_logstart(nodeid, location):
50    path = "file://" + os.path.realpath(os.path.join(CURRENT_DIR_NAME, location[0]))
51    if location[1]:
52      path += ":" +str(location[1] + 1)
53    global current_suite, current_file, current_file_suite
54    current_file = nodeid.split("::")[0]
55
56    file_suite = current_file.split("/")[-1]
57    if file_suite != current_file_suite:
58      if current_suite:
59        messages.testSuiteFinished(current_suite)
60      if current_file_suite:
61        messages.testSuiteFinished(current_file_suite)
62      current_file_suite = file_suite
63      if current_file_suite:
64        messages.testSuiteStarted(current_file_suite, location=path)
65
66    if location[2].find(".") != -1:
67      suite = location[2].split(".")[0]
68      name = location[2].split(".")[-1]
69    else:
70      name = location[2]
71      splitted = nodeid.split("::")
72      try:
73        ind = splitted.index(name.split("[")[0])
74      except ValueError:
75        try:
76          ind = splitted.index(name)
77        except ValueError:
78          ind = 0
79      if splitted[ind-1] == current_file:
80        suite = None
81      else:
82        suite = current_suite
83    if suite != current_suite:
84      if current_suite:
85        messages.testSuiteFinished(current_suite)
86      current_suite = suite
87      if current_suite:
88        messages.testSuiteStarted(current_suite, location=path)
89    messages.testStarted(name, location=path)
90    items[nodeid] = name
91
92  def pytest_runtest_logreport(report):
93    name = items[report.nodeid]
94
95    if report.skipped:
96      messages.testIgnored(name)
97    elif report.failed: # Duration should be in ms, but report has s
98      messages.testFailed(name, details=report.longrepr, duration=int(report.duration * 1000))
99    elif report.when == "call":
100      messages.testFinished(name, duration=int(report.duration * 1000))
101
102  def pytest_sessionfinish(session, exitstatus):
103    if not messages.number_of_tests and not current_suite and not current_file_suite:
104      messages.testError("ERROR", "No tests found")
105    if current_suite:
106      messages.testSuiteFinished(current_suite)
107    if current_file_suite:
108      messages.testSuiteFinished(current_file_suite)
109
110  from _pytest.terminal import TerminalReporter
111  class PycharmTestReporter(TerminalReporter):
112    def __init__(self, config, file=None):
113      TerminalReporter.__init__(self, config, file)
114
115    def summary_errors(self):
116      reports = self.getreports('error')
117      if not reports:
118        return
119      for rep in self.stats['error']:
120        name = rep.nodeid.split("/")[-1]
121        location = None
122        if hasattr(rep, 'location'):
123          location, lineno, domain = rep.location
124
125        messages.testSuiteStarted(name, location=fspath_to_url(location))
126        messages.testStarted("ERROR", location=fspath_to_url(location))
127        TerminalReporter.summary_errors(self)
128        messages.testError("ERROR")
129        messages.testSuiteFinished(name)
130
131else:
132  def pytest_collectstart(collector):
133    if collector.name != "()":
134      messages.testSuiteStarted(collector.name, location=fspath_to_url(collector.fspath))
135
136  def pytest_runtest_makereport(item, call):
137    if call.when == "setup":
138      fspath, lineno, msg = item.reportinfo()
139      url = fspath_to_url(fspath)
140      if lineno: url += ":" + str(lineno)
141    #    messages.testStarted(item.name, location=url)
142
143  def pytest_runtest_logreport(report):
144    if report.item._args:
145      name = report.item.function.__name__ + str(report.item._args)
146    else:
147      name = report.item.name
148    if report.failed:
149      messages.testFailed(name, details=report.longrepr)
150    elif report.skipped:
151      messages.testIgnored(name)
152    else:
153      messages.testFinished(name)
154
155  def pytest_collectreport(report):
156    if report.collector.name != "()":
157      messages.testSuiteFinished(report.collector.name)
158
159  def pytest_itemstart(item, node=None):
160    if item._args:
161      name = item.function.__name__ + str(item._args)
162    else:
163      name = item.name
164    if hasattr(item, "_fslineno"):
165      path = fspath_to_url(item._fslineno[0]) + ":" + str(item._fslineno[1] + 1)
166    else:
167      path = fspath_to_url(item.fspath)
168    messages.testStarted(name, location=path)
169
170
171try:
172  @pytest.hookimpl(trylast=True)
173  def pytest_configure(config):
174    reporter = PycharmTestReporter(config, sys.stdout)
175    config.pluginmanager.unregister(name="terminalreporter")
176    config.pluginmanager.register(reporter, 'terminalreporter')
177except AttributeError as e:
178  sys.stderr.write("Unable to set hookimpl. Some errors may be ignored. Make sure you use PyTest 2.8.0+. Error was {0}".format(e))