Merge lp:~thomir-deactivatedaccount/autopilot/1.3-fix-processsearcherror into lp:autopilot/1.3

Proposed by Thomi Richards
Status: Merged
Approved by: Christopher Lee
Approved revision: 350
Merged at revision: 348
Proposed branch: lp:~thomir-deactivatedaccount/autopilot/1.3-fix-processsearcherror
Merge into: lp:autopilot/1.3
Diff against target: 196 lines (+126/-9)
3 files modified
autopilot/introspection/__init__.py (+67/-6)
autopilot/tests/functional/test_ap_apps.py (+8/-2)
autopilot/tests/unit/test_exceptions.py (+51/-1)
To merge this branch: bzr merge lp:~thomir-deactivatedaccount/autopilot/1.3-fix-processsearcherror
Reviewer Review Type Date Requested Status
Christopher Lee (community) Approve
PS Jenkins bot continuous-integration Approve
Review via email: mp+191312@code.launchpad.net

Commit message

Make the ProcessSearchError exception print a more useful message.

Description of the change

Make the ProcessSearchError exception print a more useful message.

To post a comment you must log in.
350. By Thomi Richards

Remove unused import.

Revision history for this message
PS Jenkins bot (ps-jenkins) wrote :
review: Approve (continuous-integration)
Revision history for this message
PS Jenkins bot (ps-jenkins) wrote :
review: Needs Fixing (continuous-integration)
Revision history for this message
PS Jenkins bot (ps-jenkins) wrote :
review: Approve (continuous-integration)
Revision history for this message
Christopher Lee (veebers) wrote :

Looks good to me

review: Approve

Preview Diff

[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
1=== modified file 'autopilot/introspection/__init__.py'
2--- autopilot/introspection/__init__.py 2013-10-02 04:11:34 +0000
3+++ autopilot/introspection/__init__.py 2013-10-15 23:07:06 +0000
4@@ -64,7 +64,68 @@
5
6
7 class ProcessSearchError(RuntimeError):
8- pass
9+ """The ProcessSearchError is raised when something goes wrong while
10+ looking for the autopilot interface for a launched process.
11+
12+ """
13+
14+ __signal_map = {
15+ 0: 'SIG_DFL',
16+ 1: 'SIGHUP',
17+ 2: 'SIGINT',
18+ 3: 'SIGQUIT',
19+ 4: 'SIGILL',
20+ 5: 'SIGTRAP',
21+ 6: 'SIGABRT',
22+ 7: 'SIGBUS',
23+ 8: 'SIGFPE',
24+ 9: 'SIGKILL',
25+ 10: 'SIGUSR1',
26+ 11: 'SIGSEGV',
27+ 12: 'SIGUSR2',
28+ 13: 'SIGPIPE',
29+ 14: 'SIGALRM',
30+ 15: 'SIGTERM',
31+ 16: 'SIGSTKFLT',
32+ 17: 'SIGCHLD',
33+ 18: 'SIGCONT',
34+ 19: 'SIGSTOP',
35+ 20: 'SIGTSTP',
36+ 21: 'SIGTTIN',
37+ 22: 'SIGTTOU',
38+ 23: 'SIGURG',
39+ 24: 'SIGXCPU',
40+ 25: 'SIGXFSZ',
41+ 26: 'SIGVTALRM',
42+ 27: 'SIGPROF',
43+ 28: 'SIGWINCH',
44+ 29: 'SIGPOLL',
45+ 30: 'SIGPWR',
46+ 31: 'SIGSYS',
47+ }
48+
49+ @staticmethod
50+ def process_exited(process_object):
51+ """Signal that the process exited before we could find the autopilot
52+ interface.
53+
54+ :param process_object: A subprocess.Popen-like object.
55+
56+ """
57+
58+ signal_name = ""
59+ return_code = process_object.returncode
60+ if (
61+ return_code < 0 and
62+ abs(return_code) in ProcessSearchError.__signal_map
63+ ):
64+ signal_name += " (" \
65+ + ProcessSearchError.__signal_map[abs(return_code)] \
66+ + ")"
67+ return ProcessSearchError(
68+ "While searching for the autopilot DBus interface, the application"
69+ " exited with return code %d%s" % (return_code, signal_name)
70+ )
71
72
73 def get_application_launcher(app_path):
74@@ -252,7 +313,10 @@
75 raise RuntimeError("Supplied PID and process.pid do not match.")
76
77 if pid is not None and not _pid_is_running(pid):
78- raise ProcessSearchError("PID %d could not be found" % pid)
79+ raise ProcessSearchError(
80+ "Search criteria specified pid=%d, but there is no process "
81+ "running with that process id." % pid
82+ )
83
84 dbus_addresses = _get_dbus_addresses_from_search_parameters(
85 pid,
86@@ -287,10 +351,7 @@
87 _get_child_pids.reset_cache()
88 if process is not None and not _process_is_running(process):
89 return_code = process.poll()
90- raise ProcessSearchError(
91- "Process exited with exit code: %d"
92- % return_code
93- )
94+ raise ProcessSearchError.process_exited(process)
95
96 bus = _get_dbus_bus_from_string(dbus_bus)
97
98
99=== modified file 'autopilot/tests/functional/test_ap_apps.py'
100--- autopilot/tests/functional/test_ap_apps.py 2013-09-16 20:36:30 +0000
101+++ autopilot/tests/functional/test_ap_apps.py 2013-10-15 23:07:06 +0000
102@@ -114,7 +114,12 @@
103
104 self.assertThat(
105 lambda: get_proxy_object_for_existing_process(pid=pid),
106- raises(ProcessSearchError("PID %d could not be found" % pid))
107+ raises(
108+ ProcessSearchError(
109+ "Search criteria specified pid=%d, but there is no "
110+ "process running with that process id." % pid
111+ )
112+ )
113 )
114
115 def test_creating_proxy_for_segfaulted_app_failed(self):
116@@ -132,7 +137,8 @@
117 sys.exit(1)
118 """))
119
120- expected_error = "Process exited with exit code: 1"
121+ expected_error = "While searching for the autopilot DBus interface, " \
122+ "the application exited with return code 1"
123 self.assertThat(
124 lambda: self.launch_test_application(path, app_type='qt'),
125 raises(ProcessSearchError(expected_error))
126
127=== modified file 'autopilot/tests/unit/test_exceptions.py'
128--- autopilot/tests/unit/test_exceptions.py 2013-09-16 14:48:02 +0000
129+++ autopilot/tests/unit/test_exceptions.py 2013-10-15 23:07:06 +0000
130@@ -17,11 +17,13 @@
131 # along with this program. If not, see <http://www.gnu.org/licenses/>.
132 #
133
134-
135+from mock import Mock
136+import subprocess
137 from testtools import TestCase
138 from testtools.matchers import raises, Equals
139
140 from autopilot.introspection.dbus import StateNotFoundError
141+from autopilot.introspection import ProcessSearchError
142
143
144 class StateNotFoundTests(TestCase):
145@@ -73,3 +75,51 @@
146 Equals(u"State not found for class 'MyClass'"
147 " and filters {'foo': 'bar'}.")
148 )
149+
150+
151+class ProcessSearchErrorTests(TestCase):
152+
153+ def _make_fake_process(self, return_code):
154+ fake_process = Mock()
155+ fake_process.returncode = return_code
156+ return fake_process
157+
158+ def _get_all_signals(self):
159+ '''return a list of (num, name) signal pairs.
160+
161+ Calls 'kill' to determine signal names and numbers, so this should be
162+ portable across platforms where 'kill' exists.
163+
164+ '''
165+
166+ output = subprocess.check_output(['kill', '-l'])
167+ return list(enumerate(output.split(), 1))
168+
169+ def test_exception_has_correct_singal_names(self):
170+ for signal_number, signal_name in self._get_all_signals():
171+ signal_number *= -1
172+ fake_process = self._make_fake_process(signal_number)
173+ err = ProcessSearchError.process_exited(fake_process)
174+
175+ self.assertThat(
176+ str(err),
177+ Equals(
178+ "While searching for the autopilot DBus interface, "
179+ "the application exited with return code %d (%s)" % (
180+ signal_number,
181+ 'SIG' + signal_name
182+ )
183+ )
184+ )
185+
186+ def test_exception_with_no_signal(self):
187+ fake_process = self._make_fake_process(0)
188+ err = ProcessSearchError.process_exited(fake_process)
189+
190+ self.assertThat(
191+ str(err),
192+ Equals(
193+ "While searching for the autopilot DBus interface, "
194+ "the application exited with return code 0"
195+ )
196+ )

Subscribers

People subscribed via source and target branches

to all changes: