Fix test runner progress for py.test
[idea/community.git] / python / helpers / pycharm / pytest_teamcity.py
1 import os
2 import sys
3 helpers_dir = os.getenv("PYCHARM_HELPERS_DIR", sys.path[0])
4 if sys.path[0] != helpers_dir:
5     sys.path.insert(0, helpers_dir)
6
7 from tcmessages import TeamcityServiceMessages
8 from pycharm_run_utils import adjust_sys_path
9
10 adjust_sys_path(False)
11
12 # Directory where test script exist
13 CURRENT_DIR_NAME = ""
14 if 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
24 messages = TeamcityServiceMessages(prepend_linebreak=True)
25 if not "_jb_do_not_call_enter_matrix" in os.environ:
26   messages.testMatrixEntered()
27 try:
28   import pytest
29   PYVERSION = [int(x) for x in pytest.__version__.split(".")]
30 except:
31   import py
32   PYVERSION = [int(x) for x in py.__version__.split(".")]
33
34 def get_name(nodeid):
35   return nodeid.split("::")[-1]
36
37 def fspath_to_url(fspath):
38   return "file:///" + str(fspath).replace("\\", "/")
39
40 if 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("<noname>", location=fspath_to_url(location))
127         TerminalReporter.summary_errors(self)
128         messages.testError("<noname>")
129         messages.testSuiteFinished(name)
130
131 else:
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)