| Tor Norbye | 1aa2e09 | 2014-08-20 17:01:23 -0700 | [diff] [blame^] | 1 | import os |
| 2 | |
| 3 | def main(): |
| 4 | import sys |
| 5 | |
| 6 | #Separate the nose params and the pydev params. |
| 7 | pydev_params = [] |
| 8 | other_test_framework_params = [] |
| 9 | found_other_test_framework_param = None |
| 10 | |
| 11 | NOSE_PARAMS = '--nose-params' |
| 12 | PY_TEST_PARAMS = '--py-test-params' |
| 13 | |
| 14 | for arg in sys.argv[1:]: |
| 15 | if not found_other_test_framework_param and arg != NOSE_PARAMS and arg != PY_TEST_PARAMS: |
| 16 | pydev_params.append(arg) |
| 17 | |
| 18 | else: |
| 19 | if not found_other_test_framework_param: |
| 20 | found_other_test_framework_param = arg |
| 21 | else: |
| 22 | other_test_framework_params.append(arg) |
| Tor Norbye | 3a2425a | 2013-11-04 10:16:08 -0800 | [diff] [blame] | 23 | |
| 24 | |
| Tor Norbye | 1aa2e09 | 2014-08-20 17:01:23 -0700 | [diff] [blame^] | 25 | #Here we'll run either with nose or with the pydev_runfiles. |
| 26 | import pydev_runfiles |
| 27 | import pydev_runfiles_xml_rpc |
| 28 | import pydevd_constants |
| 29 | from pydevd_file_utils import _NormFile |
| Tor Norbye | 3a2425a | 2013-11-04 10:16:08 -0800 | [diff] [blame] | 30 | |
| Tor Norbye | 1aa2e09 | 2014-08-20 17:01:23 -0700 | [diff] [blame^] | 31 | DEBUG = 0 |
| 32 | if DEBUG: |
| 33 | sys.stdout.write('Received parameters: %s\n' % (sys.argv,)) |
| 34 | sys.stdout.write('Params for pydev: %s\n' % (pydev_params,)) |
| 35 | if found_other_test_framework_param: |
| 36 | sys.stdout.write('Params for test framework: %s, %s\n' % (found_other_test_framework_param, other_test_framework_params)) |
| Tor Norbye | 3a2425a | 2013-11-04 10:16:08 -0800 | [diff] [blame] | 37 | |
| Tor Norbye | 3a2425a | 2013-11-04 10:16:08 -0800 | [diff] [blame] | 38 | try: |
| Tor Norbye | 1aa2e09 | 2014-08-20 17:01:23 -0700 | [diff] [blame^] | 39 | configuration = pydev_runfiles.parse_cmdline([sys.argv[0]] + pydev_params) |
| Tor Norbye | 3a2425a | 2013-11-04 10:16:08 -0800 | [diff] [blame] | 40 | except: |
| Tor Norbye | 1aa2e09 | 2014-08-20 17:01:23 -0700 | [diff] [blame^] | 41 | sys.stderr.write('Command line received: %s\n' % (sys.argv,)) |
| 42 | raise |
| 43 | pydev_runfiles_xml_rpc.InitializeServer(configuration.port) #Note that if the port is None, a Null server will be initialized. |
| Tor Norbye | 3a2425a | 2013-11-04 10:16:08 -0800 | [diff] [blame] | 44 | |
| Tor Norbye | 1aa2e09 | 2014-08-20 17:01:23 -0700 | [diff] [blame^] | 45 | NOSE_FRAMEWORK = 1 |
| 46 | PY_TEST_FRAMEWORK = 2 |
| Tor Norbye | 3a2425a | 2013-11-04 10:16:08 -0800 | [diff] [blame] | 47 | try: |
| Tor Norbye | 1aa2e09 | 2014-08-20 17:01:23 -0700 | [diff] [blame^] | 48 | if found_other_test_framework_param: |
| 49 | test_framework = 0 #Default (pydev) |
| 50 | if found_other_test_framework_param == NOSE_PARAMS: |
| 51 | import nose |
| 52 | test_framework = NOSE_FRAMEWORK |
| 53 | |
| 54 | elif found_other_test_framework_param == PY_TEST_PARAMS: |
| 55 | import pytest |
| 56 | test_framework = PY_TEST_FRAMEWORK |
| 57 | |
| 58 | else: |
| 59 | raise ImportError() |
| 60 | |
| 61 | else: |
| 62 | raise ImportError() |
| 63 | |
| 64 | except ImportError: |
| 65 | if found_other_test_framework_param: |
| 66 | sys.stderr.write('Warning: Could not import the test runner: %s. Running with the default pydev unittest runner instead.\n' % ( |
| 67 | found_other_test_framework_param,)) |
| 68 | |
| 69 | test_framework = 0 |
| 70 | |
| 71 | #Clear any exception that may be there so that clients don't see it. |
| 72 | #See: https://sourceforge.net/tracker/?func=detail&aid=3408057&group_id=85796&atid=577329 |
| 73 | if hasattr(sys, 'exc_clear'): |
| 74 | sys.exc_clear() |
| 75 | |
| 76 | if test_framework == 0: |
| 77 | |
| 78 | pydev_runfiles.main(configuration) |
| 79 | |
| Tor Norbye | 3a2425a | 2013-11-04 10:16:08 -0800 | [diff] [blame] | 80 | else: |
| Tor Norbye | 1aa2e09 | 2014-08-20 17:01:23 -0700 | [diff] [blame^] | 81 | #We'll convert the parameters to what nose or py.test expects. |
| 82 | #The supported parameters are: |
| 83 | #runfiles.py --config-file|-t|--tests <Test.test1,Test2> dirs|files --nose-params xxx yyy zzz |
| 84 | #(all after --nose-params should be passed directly to nose) |
| Tor Norbye | 3a2425a | 2013-11-04 10:16:08 -0800 | [diff] [blame] | 85 | |
| Tor Norbye | 1aa2e09 | 2014-08-20 17:01:23 -0700 | [diff] [blame^] | 86 | #In java: |
| 87 | #--tests = Constants.ATTR_UNITTEST_TESTS |
| 88 | #--config-file = Constants.ATTR_UNITTEST_CONFIGURATION_FILE |
| Tor Norbye | 3a2425a | 2013-11-04 10:16:08 -0800 | [diff] [blame] | 89 | |
| 90 | |
| Tor Norbye | 1aa2e09 | 2014-08-20 17:01:23 -0700 | [diff] [blame^] | 91 | #The only thing actually handled here are the tests that we want to run, which we'll |
| 92 | #handle and pass as what the test framework expects. |
| Tor Norbye | 3a2425a | 2013-11-04 10:16:08 -0800 | [diff] [blame] | 93 | |
| Tor Norbye | 1aa2e09 | 2014-08-20 17:01:23 -0700 | [diff] [blame^] | 94 | py_test_accept_filter = {} |
| 95 | files_to_tests = configuration.files_to_tests |
| Tor Norbye | 3a2425a | 2013-11-04 10:16:08 -0800 | [diff] [blame] | 96 | |
| Tor Norbye | 1aa2e09 | 2014-08-20 17:01:23 -0700 | [diff] [blame^] | 97 | if files_to_tests: |
| 98 | #Handling through the file contents (file where each line is a test) |
| 99 | files_or_dirs = [] |
| 100 | for file, tests in files_to_tests.items(): |
| 101 | if test_framework == NOSE_FRAMEWORK: |
| 102 | for test in tests: |
| 103 | files_or_dirs.append(file + ':' + test) |
| Tor Norbye | 3a2425a | 2013-11-04 10:16:08 -0800 | [diff] [blame] | 104 | |
| Tor Norbye | 1aa2e09 | 2014-08-20 17:01:23 -0700 | [diff] [blame^] | 105 | elif test_framework == PY_TEST_FRAMEWORK: |
| 106 | file = _NormFile(file) |
| 107 | py_test_accept_filter[file] = tests |
| 108 | files_or_dirs.append(file) |
| Tor Norbye | 3a2425a | 2013-11-04 10:16:08 -0800 | [diff] [blame] | 109 | |
| Tor Norbye | 3a2425a | 2013-11-04 10:16:08 -0800 | [diff] [blame] | 110 | else: |
| Tor Norbye | 1aa2e09 | 2014-08-20 17:01:23 -0700 | [diff] [blame^] | 111 | raise AssertionError('Cannot handle test framework: %s at this point.' % (test_framework,)) |
| Tor Norbye | 3a2425a | 2013-11-04 10:16:08 -0800 | [diff] [blame] | 112 | |
| Tor Norbye | 1aa2e09 | 2014-08-20 17:01:23 -0700 | [diff] [blame^] | 113 | else: |
| 114 | if configuration.tests: |
| 115 | #Tests passed (works together with the files_or_dirs) |
| 116 | files_or_dirs = [] |
| 117 | for file in configuration.files_or_dirs: |
| 118 | if test_framework == NOSE_FRAMEWORK: |
| 119 | for t in configuration.tests: |
| 120 | files_or_dirs.append(file + ':' + t) |
| Tor Norbye | 3a2425a | 2013-11-04 10:16:08 -0800 | [diff] [blame] | 121 | |
| Tor Norbye | 1aa2e09 | 2014-08-20 17:01:23 -0700 | [diff] [blame^] | 122 | elif test_framework == PY_TEST_FRAMEWORK: |
| 123 | file = _NormFile(file) |
| 124 | py_test_accept_filter[file] = configuration.tests |
| 125 | files_or_dirs.append(file) |
| Tor Norbye | 3a2425a | 2013-11-04 10:16:08 -0800 | [diff] [blame] | 126 | |
| 127 | else: |
| Tor Norbye | 1aa2e09 | 2014-08-20 17:01:23 -0700 | [diff] [blame^] | 128 | raise AssertionError('Cannot handle test framework: %s at this point.' % (test_framework,)) |
| 129 | else: |
| 130 | #Only files or dirs passed (let it do the test-loading based on those paths) |
| 131 | files_or_dirs = configuration.files_or_dirs |
| Tor Norbye | 3a2425a | 2013-11-04 10:16:08 -0800 | [diff] [blame] | 132 | |
| Tor Norbye | 1aa2e09 | 2014-08-20 17:01:23 -0700 | [diff] [blame^] | 133 | argv = other_test_framework_params + files_or_dirs |
| Tor Norbye | 3a2425a | 2013-11-04 10:16:08 -0800 | [diff] [blame] | 134 | |
| 135 | |
| Tor Norbye | 1aa2e09 | 2014-08-20 17:01:23 -0700 | [diff] [blame^] | 136 | if test_framework == NOSE_FRAMEWORK: |
| 137 | #Nose usage: http://somethingaboutorange.com/mrl/projects/nose/0.11.2/usage.html |
| 138 | #show_stdout_option = ['-s'] |
| 139 | #processes_option = ['--processes=2'] |
| 140 | argv.insert(0, sys.argv[0]) |
| 141 | if DEBUG: |
| 142 | sys.stdout.write('Final test framework args: %s\n' % (argv[1:],)) |
| 143 | |
| 144 | import pydev_runfiles_nose |
| 145 | PYDEV_NOSE_PLUGIN_SINGLETON = pydev_runfiles_nose.StartPydevNosePluginSingleton(configuration) |
| 146 | argv.append('--with-pydevplugin') |
| 147 | nose.run(argv=argv, addplugins=[PYDEV_NOSE_PLUGIN_SINGLETON]) |
| 148 | |
| 149 | elif test_framework == PY_TEST_FRAMEWORK: |
| 150 | if DEBUG: |
| 151 | sys.stdout.write('Final test framework args: %s\n' % (argv,)) |
| 152 | sys.stdout.write('py_test_accept_filter: %s\n' % (py_test_accept_filter,)) |
| 153 | |
| 154 | import os |
| 155 | |
| 156 | try: |
| 157 | xrange |
| 158 | except: |
| 159 | xrange = range |
| 160 | |
| 161 | for i in xrange(len(argv)): |
| 162 | arg = argv[i] |
| 163 | #Workaround bug in py.test: if we pass the full path it ends up importing conftest |
| 164 | #more than once (so, always work with relative paths). |
| 165 | if os.path.isfile(arg) or os.path.isdir(arg): |
| 166 | from pydev_imports import relpath |
| 167 | arg = relpath(arg) |
| 168 | argv[i] = arg |
| 169 | |
| 170 | d = os.path.dirname(__file__) |
| 171 | if d not in sys.path: |
| 172 | sys.path.insert(0, d) |
| 173 | |
| 174 | import pickle, zlib, base64 |
| 175 | |
| 176 | # Update environment PYTHONPATH so that it finds our plugin if using xdist. |
| 177 | os.environ['PYTHONPATH'] = os.pathsep.join(sys.path) |
| 178 | |
| 179 | # Set what should be skipped in the plugin through an environment variable |
| 180 | s = base64.b64encode(zlib.compress(pickle.dumps(py_test_accept_filter))) |
| 181 | if pydevd_constants.IS_PY3K: |
| 182 | s = s.decode('ascii') # Must be str in py3. |
| 183 | os.environ['PYDEV_PYTEST_SKIP'] = s |
| 184 | |
| 185 | # Identifies the main pid (i.e.: if it's not the main pid it has to connect back to the |
| 186 | # main pid to give xml-rpc notifications). |
| 187 | os.environ['PYDEV_MAIN_PID'] = str(os.getpid()) |
| 188 | os.environ['PYDEV_PYTEST_SERVER'] = str(configuration.port) |
| 189 | |
| 190 | argv.append('-p') |
| 191 | argv.append('pydev_runfiles_pytest2') |
| 192 | pytest.main(argv) |
| 193 | |
| 194 | else: |
| 195 | raise AssertionError('Cannot handle test framework: %s at this point.' % (test_framework,)) |
| Tor Norbye | 3a2425a | 2013-11-04 10:16:08 -0800 | [diff] [blame] | 196 | |
| 197 | |
| Tor Norbye | 3a2425a | 2013-11-04 10:16:08 -0800 | [diff] [blame] | 198 | if __name__ == '__main__': |
| Tor Norbye | 1aa2e09 | 2014-08-20 17:01:23 -0700 | [diff] [blame^] | 199 | try: |
| 200 | main() |
| 201 | finally: |
| 202 | try: |
| 203 | #The server is not a daemon thread, so, we have to ask for it to be killed! |
| 204 | import pydev_runfiles_xml_rpc |
| 205 | pydev_runfiles_xml_rpc.forceServerKill() |
| 206 | except: |
| 207 | pass #Ignore any errors here |
| 208 | |
| 209 | import sys |
| 210 | import threading |
| 211 | if hasattr(sys, '_current_frames') and hasattr(threading, 'enumerate'): |
| 212 | import time |
| 213 | import traceback |
| 214 | |
| 215 | class DumpThreads(threading.Thread): |
| 216 | def run(self): |
| 217 | time.sleep(10) |
| 218 | |
| 219 | thread_id_to_name = {} |
| 220 | try: |
| 221 | for t in threading.enumerate(): |
| 222 | thread_id_to_name[t.ident] = '%s (daemon: %s)' % (t.name, t.daemon) |
| 223 | except: |
| 224 | pass |
| 225 | |
| 226 | stack_trace = [ |
| 227 | '===============================================================================', |
| 228 | 'pydev pyunit runner: Threads still found running after tests finished', |
| 229 | '================================= Thread Dump ================================='] |
| 230 | |
| 231 | for thread_id, stack in sys._current_frames().items(): |
| 232 | stack_trace.append('\n-------------------------------------------------------------------------------') |
| 233 | stack_trace.append(" Thread %s" % thread_id_to_name.get(thread_id, thread_id)) |
| 234 | stack_trace.append('') |
| 235 | |
| 236 | if 'self' in stack.f_locals: |
| 237 | sys.stderr.write(str(stack.f_locals['self'])+'\n') |
| 238 | |
| 239 | for filename, lineno, name, line in traceback.extract_stack(stack): |
| 240 | stack_trace.append(' File "%s", line %d, in %s' % (filename, lineno, name)) |
| 241 | if line: |
| 242 | stack_trace.append(" %s" % (line.strip())) |
| 243 | stack_trace.append('\n=============================== END Thread Dump ===============================') |
| 244 | sys.stderr.write('\n'.join(stack_trace)) |
| 245 | |
| 246 | |
| 247 | dump_current_frames_thread = DumpThreads() |
| 248 | dump_current_frames_thread.setDaemon(True) # Daemon so that this thread doesn't halt it! |
| 249 | dump_current_frames_thread.start() |