##// END OF EJS Templates
monkeypatch RemoteError to include tracebacks during tests
MinRK -
Show More
@@ -1,136 +1,145 b''
1 """toplevel setup/teardown for parallel tests."""
1 """toplevel setup/teardown for parallel tests."""
2 from __future__ import print_function
2 from __future__ import print_function
3
3
4 #-------------------------------------------------------------------------------
4 #-------------------------------------------------------------------------------
5 # Copyright (C) 2011 The IPython Development Team
5 # Copyright (C) 2011 The IPython Development Team
6 #
6 #
7 # Distributed under the terms of the BSD License. The full license is in
7 # Distributed under the terms of the BSD License. The full license is in
8 # the file COPYING, distributed as part of this software.
8 # the file COPYING, distributed as part of this software.
9 #-------------------------------------------------------------------------------
9 #-------------------------------------------------------------------------------
10
10
11 #-------------------------------------------------------------------------------
11 #-------------------------------------------------------------------------------
12 # Imports
12 # Imports
13 #-------------------------------------------------------------------------------
13 #-------------------------------------------------------------------------------
14
14
15 import os
15 import os
16 import tempfile
16 import tempfile
17 import time
17 import time
18 from subprocess import Popen, PIPE, STDOUT
18 from subprocess import Popen, PIPE, STDOUT
19
19
20 import nose
20 import nose
21
21
22 from IPython.utils.path import get_ipython_dir
22 from IPython.utils.path import get_ipython_dir
23 from IPython.parallel import Client
23 from IPython.parallel import Client, error
24 from IPython.parallel.apps.launcher import (LocalProcessLauncher,
24 from IPython.parallel.apps.launcher import (LocalProcessLauncher,
25 ipengine_cmd_argv,
25 ipengine_cmd_argv,
26 ipcontroller_cmd_argv,
26 ipcontroller_cmd_argv,
27 SIGKILL,
27 SIGKILL,
28 ProcessStateError,
28 ProcessStateError,
29 )
29 )
30
30
31 # globals
31 # globals
32 launchers = []
32 launchers = []
33 blackhole = open(os.devnull, 'w')
33 blackhole = open(os.devnull, 'w')
34
34
35 # Launcher class
35 # Launcher class
36 class TestProcessLauncher(LocalProcessLauncher):
36 class TestProcessLauncher(LocalProcessLauncher):
37 """subclass LocalProcessLauncher, to prevent extra sockets and threads being created on Windows"""
37 """subclass LocalProcessLauncher, to prevent extra sockets and threads being created on Windows"""
38 def start(self):
38 def start(self):
39 if self.state == 'before':
39 if self.state == 'before':
40 # Store stdout & stderr to show with failing tests.
40 # Store stdout & stderr to show with failing tests.
41 # This is defined in IPython.testing.iptest
41 # This is defined in IPython.testing.iptest
42 self.process = Popen(self.args,
42 self.process = Popen(self.args,
43 stdout=nose.iptest_stdstreams_fileno(), stderr=STDOUT,
43 stdout=nose.iptest_stdstreams_fileno(), stderr=STDOUT,
44 env=os.environ,
44 env=os.environ,
45 cwd=self.work_dir
45 cwd=self.work_dir
46 )
46 )
47 self.notify_start(self.process.pid)
47 self.notify_start(self.process.pid)
48 self.poll = self.process.poll
48 self.poll = self.process.poll
49 else:
49 else:
50 s = 'The process was already started and has state: %r' % self.state
50 s = 'The process was already started and has state: %r' % self.state
51 raise ProcessStateError(s)
51 raise ProcessStateError(s)
52
52
53 # nose setup/teardown
53 # nose setup/teardown
54
54
55 def setup():
55 def setup():
56
57 # show tracebacks for RemoteErrors
58 class RemoteErrorWithTB(error.RemoteError):
59 def __str__(self):
60 s = super(RemoteErrorWithTB, self).__str__()
61 return '\n'.join([s, self.traceback or ''])
62
63 error.RemoteError = RemoteErrorWithTB
64
56 cluster_dir = os.path.join(get_ipython_dir(), 'profile_iptest')
65 cluster_dir = os.path.join(get_ipython_dir(), 'profile_iptest')
57 engine_json = os.path.join(cluster_dir, 'security', 'ipcontroller-engine.json')
66 engine_json = os.path.join(cluster_dir, 'security', 'ipcontroller-engine.json')
58 client_json = os.path.join(cluster_dir, 'security', 'ipcontroller-client.json')
67 client_json = os.path.join(cluster_dir, 'security', 'ipcontroller-client.json')
59 for json in (engine_json, client_json):
68 for json in (engine_json, client_json):
60 if os.path.exists(json):
69 if os.path.exists(json):
61 os.remove(json)
70 os.remove(json)
62
71
63 cp = TestProcessLauncher()
72 cp = TestProcessLauncher()
64 cp.cmd_and_args = ipcontroller_cmd_argv + \
73 cp.cmd_and_args = ipcontroller_cmd_argv + \
65 ['--profile=iptest', '--log-level=20', '--ping=250', '--dictdb']
74 ['--profile=iptest', '--log-level=20', '--ping=250', '--dictdb']
66 cp.start()
75 cp.start()
67 launchers.append(cp)
76 launchers.append(cp)
68 tic = time.time()
77 tic = time.time()
69 while not os.path.exists(engine_json) or not os.path.exists(client_json):
78 while not os.path.exists(engine_json) or not os.path.exists(client_json):
70 if cp.poll() is not None:
79 if cp.poll() is not None:
71 raise RuntimeError("The test controller exited with status %s" % cp.poll())
80 raise RuntimeError("The test controller exited with status %s" % cp.poll())
72 elif time.time()-tic > 15:
81 elif time.time()-tic > 15:
73 raise RuntimeError("Timeout waiting for the test controller to start.")
82 raise RuntimeError("Timeout waiting for the test controller to start.")
74 time.sleep(0.1)
83 time.sleep(0.1)
75 add_engines(1)
84 add_engines(1)
76
85
77 def add_engines(n=1, profile='iptest', total=False):
86 def add_engines(n=1, profile='iptest', total=False):
78 """add a number of engines to a given profile.
87 """add a number of engines to a given profile.
79
88
80 If total is True, then already running engines are counted, and only
89 If total is True, then already running engines are counted, and only
81 the additional engines necessary (if any) are started.
90 the additional engines necessary (if any) are started.
82 """
91 """
83 rc = Client(profile=profile)
92 rc = Client(profile=profile)
84 base = len(rc)
93 base = len(rc)
85
94
86 if total:
95 if total:
87 n = max(n - base, 0)
96 n = max(n - base, 0)
88
97
89 eps = []
98 eps = []
90 for i in range(n):
99 for i in range(n):
91 ep = TestProcessLauncher()
100 ep = TestProcessLauncher()
92 ep.cmd_and_args = ipengine_cmd_argv + [
101 ep.cmd_and_args = ipengine_cmd_argv + [
93 '--profile=%s' % profile,
102 '--profile=%s' % profile,
94 '--log-level=50',
103 '--log-level=50',
95 '--InteractiveShell.colors=nocolor'
104 '--InteractiveShell.colors=nocolor'
96 ]
105 ]
97 ep.start()
106 ep.start()
98 launchers.append(ep)
107 launchers.append(ep)
99 eps.append(ep)
108 eps.append(ep)
100 tic = time.time()
109 tic = time.time()
101 while len(rc) < base+n:
110 while len(rc) < base+n:
102 if any([ ep.poll() is not None for ep in eps ]):
111 if any([ ep.poll() is not None for ep in eps ]):
103 raise RuntimeError("A test engine failed to start.")
112 raise RuntimeError("A test engine failed to start.")
104 elif time.time()-tic > 15:
113 elif time.time()-tic > 15:
105 raise RuntimeError("Timeout waiting for engines to connect.")
114 raise RuntimeError("Timeout waiting for engines to connect.")
106 time.sleep(.1)
115 time.sleep(.1)
107 rc.spin()
116 rc.spin()
108 rc.close()
117 rc.close()
109 return eps
118 return eps
110
119
111 def teardown():
120 def teardown():
112 try:
121 try:
113 time.sleep(1)
122 time.sleep(1)
114 except KeyboardInterrupt:
123 except KeyboardInterrupt:
115 return
124 return
116 while launchers:
125 while launchers:
117 p = launchers.pop()
126 p = launchers.pop()
118 if p.poll() is None:
127 if p.poll() is None:
119 try:
128 try:
120 p.stop()
129 p.stop()
121 except Exception as e:
130 except Exception as e:
122 print(e)
131 print(e)
123 pass
132 pass
124 if p.poll() is None:
133 if p.poll() is None:
125 try:
134 try:
126 time.sleep(.25)
135 time.sleep(.25)
127 except KeyboardInterrupt:
136 except KeyboardInterrupt:
128 return
137 return
129 if p.poll() is None:
138 if p.poll() is None:
130 try:
139 try:
131 print('cleaning up test process...')
140 print('cleaning up test process...')
132 p.signal(SIGKILL)
141 p.signal(SIGKILL)
133 except:
142 except:
134 print("couldn't shutdown process: ", p)
143 print("couldn't shutdown process: ", p)
135 blackhole.close()
144 blackhole.close()
136
145
General Comments 0
You need to be logged in to leave comments. Login now