##// END OF EJS Templates
catch KeyboardInterrupt in parallel tests teardown...
MinRK -
Show More
@@ -1,130 +1,136 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
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 cluster_dir = os.path.join(get_ipython_dir(), 'profile_iptest')
56 cluster_dir = os.path.join(get_ipython_dir(), 'profile_iptest')
57 engine_json = os.path.join(cluster_dir, 'security', 'ipcontroller-engine.json')
57 engine_json = os.path.join(cluster_dir, 'security', 'ipcontroller-engine.json')
58 client_json = os.path.join(cluster_dir, 'security', 'ipcontroller-client.json')
58 client_json = os.path.join(cluster_dir, 'security', 'ipcontroller-client.json')
59 for json in (engine_json, client_json):
59 for json in (engine_json, client_json):
60 if os.path.exists(json):
60 if os.path.exists(json):
61 os.remove(json)
61 os.remove(json)
62
62
63 cp = TestProcessLauncher()
63 cp = TestProcessLauncher()
64 cp.cmd_and_args = ipcontroller_cmd_argv + \
64 cp.cmd_and_args = ipcontroller_cmd_argv + \
65 ['--profile=iptest', '--log-level=20', '--ping=250', '--dictdb']
65 ['--profile=iptest', '--log-level=20', '--ping=250', '--dictdb']
66 cp.start()
66 cp.start()
67 launchers.append(cp)
67 launchers.append(cp)
68 tic = time.time()
68 tic = time.time()
69 while not os.path.exists(engine_json) or not os.path.exists(client_json):
69 while not os.path.exists(engine_json) or not os.path.exists(client_json):
70 if cp.poll() is not None:
70 if cp.poll() is not None:
71 raise RuntimeError("The test controller exited with status %s" % cp.poll())
71 raise RuntimeError("The test controller exited with status %s" % cp.poll())
72 elif time.time()-tic > 15:
72 elif time.time()-tic > 15:
73 raise RuntimeError("Timeout waiting for the test controller to start.")
73 raise RuntimeError("Timeout waiting for the test controller to start.")
74 time.sleep(0.1)
74 time.sleep(0.1)
75 add_engines(1)
75 add_engines(1)
76
76
77 def add_engines(n=1, profile='iptest', total=False):
77 def add_engines(n=1, profile='iptest', total=False):
78 """add a number of engines to a given profile.
78 """add a number of engines to a given profile.
79
79
80 If total is True, then already running engines are counted, and only
80 If total is True, then already running engines are counted, and only
81 the additional engines necessary (if any) are started.
81 the additional engines necessary (if any) are started.
82 """
82 """
83 rc = Client(profile=profile)
83 rc = Client(profile=profile)
84 base = len(rc)
84 base = len(rc)
85
85
86 if total:
86 if total:
87 n = max(n - base, 0)
87 n = max(n - base, 0)
88
88
89 eps = []
89 eps = []
90 for i in range(n):
90 for i in range(n):
91 ep = TestProcessLauncher()
91 ep = TestProcessLauncher()
92 ep.cmd_and_args = ipengine_cmd_argv + [
92 ep.cmd_and_args = ipengine_cmd_argv + [
93 '--profile=%s' % profile,
93 '--profile=%s' % profile,
94 '--log-level=50',
94 '--log-level=50',
95 '--InteractiveShell.colors=nocolor'
95 '--InteractiveShell.colors=nocolor'
96 ]
96 ]
97 ep.start()
97 ep.start()
98 launchers.append(ep)
98 launchers.append(ep)
99 eps.append(ep)
99 eps.append(ep)
100 tic = time.time()
100 tic = time.time()
101 while len(rc) < base+n:
101 while len(rc) < base+n:
102 if any([ ep.poll() is not None for ep in eps ]):
102 if any([ ep.poll() is not None for ep in eps ]):
103 raise RuntimeError("A test engine failed to start.")
103 raise RuntimeError("A test engine failed to start.")
104 elif time.time()-tic > 15:
104 elif time.time()-tic > 15:
105 raise RuntimeError("Timeout waiting for engines to connect.")
105 raise RuntimeError("Timeout waiting for engines to connect.")
106 time.sleep(.1)
106 time.sleep(.1)
107 rc.spin()
107 rc.spin()
108 rc.close()
108 rc.close()
109 return eps
109 return eps
110
110
111 def teardown():
111 def teardown():
112 try:
112 time.sleep(1)
113 time.sleep(1)
114 except KeyboardInterrupt:
115 return
113 while launchers:
116 while launchers:
114 p = launchers.pop()
117 p = launchers.pop()
115 if p.poll() is None:
118 if p.poll() is None:
116 try:
119 try:
117 p.stop()
120 p.stop()
118 except Exception as e:
121 except Exception as e:
119 print(e)
122 print(e)
120 pass
123 pass
121 if p.poll() is None:
124 if p.poll() is None:
125 try:
122 time.sleep(.25)
126 time.sleep(.25)
127 except KeyboardInterrupt:
128 return
123 if p.poll() is None:
129 if p.poll() is None:
124 try:
130 try:
125 print('cleaning up test process...')
131 print('cleaning up test process...')
126 p.signal(SIGKILL)
132 p.signal(SIGKILL)
127 except:
133 except:
128 print("couldn't shutdown process: ", p)
134 print("couldn't shutdown process: ", p)
129 blackhole.close()
135 blackhole.close()
130
136
General Comments 0
You need to be logged in to leave comments. Login now