Merge lp:~mikemc/ubuntu-sso-client/fix-networkstate-darwin into lp:ubuntu-sso-client
- fix-networkstate-darwin
- Merge into trunk
Status: | Merged | ||||
---|---|---|---|---|---|
Approved by: | Mike McCracken | ||||
Approved revision: | 975 | ||||
Merged at revision: | 961 | ||||
Proposed branch: | lp:~mikemc/ubuntu-sso-client/fix-networkstate-darwin | ||||
Merge into: | lp:ubuntu-sso-client | ||||
Prerequisite: | lp:~mikemc/ubuntu-sso-client/no-darwin-in-linuxnwindows-tests | ||||
Diff against target: |
630 lines (+514/-68) 4 files modified
ubuntu_sso/networkstate/darwin.py (+269/-61) ubuntu_sso/networkstate/networkstates.py (+13/-7) ubuntu_sso/networkstate/tests/run_nwmgr_standalone.py (+39/-0) ubuntu_sso/networkstate/tests/test_darwin.py (+193/-0) |
||||
To merge this branch: | bzr merge lp:~mikemc/ubuntu-sso-client/fix-networkstate-darwin | ||||
Related bugs: |
|
Reviewer | Review Type | Date Requested | Status |
---|---|---|---|
Diego Sarmentero (community) | Approve | ||
Manuel de la Peña (community) | Approve | ||
Review via email: mp+105926@code.launchpad.net |
Commit message
- Implement internet connectivity detection in Mac OS X ("darwin") systems ( LP #984667 )
Description of the change
- Implement internet connectivity detection in Mac OS X ("darwin") systems ( LP #984667 )
Implementation of network detection for darwin, including tests.
Uses a similar approach to the windows module, except instead of checking general internet connectivity, it specifically checks connectivity to one.ubuntu.com.
There's no general "connected" API in OS X, and I'm not sure there should be in Windows.
There are two API entry points:
1. The NetworkManagerState class spawns a separate python thread
for listening for callbacks from the system API, which then calls back
to user code with one of the states defined in
networkstate/
2. The is_machine_
calls the synchronous API to check the current connectivity once.
This branch includes two other changes:
1. run-nwmgr-
isolation. It should be platform-
report on connection status for 60 seconds.
2. networkstates.py now defines ONLINE, OFFLINE, and UNKNOWN as objects with meaningful __repr__s, so that test failure output makes sense.
This branch was marked as dependent on the no-darwin-
- 970. By Mike McCracken
-
merge with linux and windows test script branch so you can test this branch on linux and windows
Mike McCracken (mikemc) wrote : | # |
> When trying to run the tests on linux I get the following import error:
>
> *** Running GTK test suite for ubuntu_sso ***
...snip...
> File "/home/
> darwin/
> CFRunLoopGetCurrent = CoreFoundation.
> File "/usr/lib/
> func = self.__
> File "/usr/lib/
> func = self._FuncPtr(
> AttributeError: /usr/bin/python: undefined symbol: CFRunLoopGetCurrent
>
>
> Probably there is a missing module in the list of ingored modules
It's running the darwin tests. The prerequisite branch fixes this, and I've now merged with that branch with commit 970. Sorry for the inconvenience. I should've used the bzr pipeline plugin, I guess...
Manuel de la Peña (mandel) wrote : | # |
> > When trying to run the tests on linux I get the following import error:
> >
> > *** Running GTK test suite for ubuntu_sso ***
>
> ...snip...
>
> > File "/home/
> > darwin/
> > CFRunLoopGetCurrent = CoreFoundation.
> > File "/usr/lib/
> > func = self.__
> > File "/usr/lib/
> > func = self._FuncPtr(
> > AttributeError: /usr/bin/python: undefined symbol: CFRunLoopGetCurrent
> >
> >
> > Probably there is a missing module in the list of ingored modules
>
> It's running the darwin tests. The prerequisite branch fixes this, and I've
> now merged with that branch with commit 970. Sorry for the inconvenience. I
> should've used the bzr pipeline plugin, I guess...
No worries about that is a small details. After merging the other branch I get the following lint errors:
ubuntu_
68: [C0301] Line too long (80/79)
253: [C0301] Line too long (81/79)
254: [C0301] Line too long (82/79)
51: [C0103] Invalid name "CoreFoundation
52: [C0103] Invalid name "CoreFoundation" (should match (([A-Z_
55: [C0103] Invalid name "CFRunLoopGetCu
60: [C0103] Invalid name "CFRelease" (should match (([A-Z_
65: [C0103] Invalid name "CFRunLoopRun" (should match (([A-Z_
68: [C0103] Invalid name "kCFRunLoopDefa
68: [E1101] Class 'c_void_p' has no 'in_dll' member
73: [C0103] Invalid name "SystemConfigur
82: [C0103] Invalid name "SCNRCreateWith
87: [C0103] Invalid name "SCNRGetFlags" (should match (([A-Z_
92: [C0103] Invalid name "SCNRScheduleWi
102: [C0103] Invalid name "SCNRCallbackType" (should match (([A-Z_
108: [C0103] Invalid name "SCNRSetCallback" (should match (([A-Z_
115: [C0111, _check_
145: [C0103, flags_say_
146: [C0103, flags_say_
270: [W0612, NetworkManagerS
282: [W0703, is_machine_
23: [W0611] Unused import NM_STATE_
30: [W0611] Unused import NM_STATE_
23: [W0611] Unused import NM_STATE_
23: [W0611] Unused import NM_STATE_
ubuntu_
18: [C0111, NetworkState] Missing docstrin...
- 971. By Mike McCracken
-
fix dumb typo in tests that are only run on darwin
- 972. By Mike McCracken
-
Fix lint and pep8 complaints, improve some names and docstrings
- 973. By Mike McCracken
-
fix whitespace in blank lines that darwin lint didn't notice
- 974. By Mike McCracken
-
fixing yet another pep8 complaint missed by pep8 on darwin
Manuel de la Peña (mandel) wrote : | # |
Can you fix the following details:
* if target == None: => if target is None
* if ok == False: => if not ok:
* if check_connected
* self.assertEqua
* self.assertEqua
* from ubuntu_
Tests and lint checks pass, please let me know when I can take another look.
- 975. By Mike McCracken
-
style fixes
Preview Diff
1 | === modified file 'ubuntu_sso/networkstate/darwin.py' |
2 | --- ubuntu_sso/networkstate/darwin.py 2012-04-23 12:22:03 +0000 |
3 | +++ ubuntu_sso/networkstate/darwin.py 2012-05-21 14:51:18 +0000 |
4 | @@ -13,83 +13,291 @@ |
5 | # |
6 | # You should have received a copy of the GNU General Public License along |
7 | # with this program. If not, see <http://www.gnu.org/licenses/>. |
8 | -"""Implementation of network state detection.""" |
9 | +"""Network state detection on OS X. |
10 | + |
11 | +is_machine_connected(): (deferred) returns connected state as bool |
12 | +NetworkManagerState: class with listening thread, calls back with state changes |
13 | +""" |
14 | |
15 | from twisted.internet import defer |
16 | |
17 | +from threading import Thread |
18 | + |
19 | from ubuntu_sso.networkstate import NetworkFailException |
20 | -from ubuntu_sso.networkstate.networkstates import ( |
21 | - ONLINE, OFFLINE, UNKNOWN, |
22 | - |
23 | - NM_STATE_CONNECTING_LIST, |
24 | - NM_STATE_CONNECTED_LIST, |
25 | - NM_STATE_DISCONNECTED_LIST, |
26 | - ) |
27 | -from ubuntu_sso.networkstate.networkstates import NM_STATE_CONNECTED_GLOBAL |
28 | +from ubuntu_sso.networkstate.networkstates import (ONLINE, OFFLINE, UNKNOWN) |
29 | from ubuntu_sso.logger import setup_logging |
30 | logger = setup_logging("ubuntu_sso.networkstate") |
31 | |
32 | +HOSTNAME_TO_CHECK = 'one.ubuntu.com' |
33 | + |
34 | +from ctypes import ( |
35 | + CDLL, |
36 | + POINTER, |
37 | + CFUNCTYPE, |
38 | + Structure, |
39 | + pointer, |
40 | + c_bool, |
41 | + c_long, |
42 | + c_void_p, |
43 | + c_uint32) |
44 | + |
45 | +from ctypes.util import find_library |
46 | + |
47 | +# pylint: disable=C0103 |
48 | + |
49 | +# Functions and constants below are from |
50 | +# /System/Library/CoreFoundation.framework/ |
51 | +CoreFoundationPath = find_library("CoreFoundation") |
52 | +CoreFoundation = CDLL(CoreFoundationPath) |
53 | + |
54 | +# CFRunLoopRef CFRunLoopGetCurrent() |
55 | +CFRunLoopGetCurrent = CoreFoundation.CFRunLoopGetCurrent |
56 | +CFRunLoopGetCurrent.restype = c_void_p |
57 | +CFRunLoopGetCurrent.argtypes = [] |
58 | + |
59 | +# void CFRelease(CFTypeRef) |
60 | +CFRelease = CoreFoundation.CFRelease |
61 | +CFRelease.restype = None |
62 | +CFRelease.argtypes = [c_void_p] |
63 | + |
64 | +# void CFRunLoopRun() |
65 | +CFRunLoopRun = CoreFoundation.CFRunLoopRun |
66 | + |
67 | +# const CFStringRef kCFRunLoopDefaultMode |
68 | +# pylint: disable=E1101 |
69 | +kCFRunLoopDefaultMode = c_void_p.in_dll(CoreFoundation, |
70 | + "kCFRunLoopDefaultMode") |
71 | + |
72 | + |
73 | +# Functions and constants below are from |
74 | +# /System/Library/SystemConfiguration.framework/ |
75 | +SystemConfigurationPath = find_library("SystemConfiguration") |
76 | + |
77 | +# SystemConfiguration abbreviated as "SC" below: |
78 | +SC = CDLL(SystemConfigurationPath) |
79 | + |
80 | +# "SCNetworkReachability" functions abbreviated to "SCNR*" here. |
81 | + |
82 | +# SCNetworkReachabilityRef |
83 | +# SCNetworkReachabilityCreateWithName(CFAllocatorRef, const char *) |
84 | +SCNRCreateWithName = SC.SCNetworkReachabilityCreateWithName |
85 | +SCNRCreateWithName.restype = c_void_p |
86 | + |
87 | +# Boolean SCNetworkReachabilityGetFlags(SCNetworkReachabilityRef, |
88 | +# SCNetworkReachabilityFlags) |
89 | +SCNRGetFlags = SC.SCNetworkReachabilityGetFlags |
90 | +SCNRGetFlags.restype = c_bool |
91 | +SCNRGetFlags.argtypes = [c_void_p, |
92 | + POINTER(c_uint32)] |
93 | + |
94 | +SCNRScheduleWithRunLoop = SC.SCNetworkReachabilityScheduleWithRunLoop |
95 | +SCNRScheduleWithRunLoop.restype = c_bool |
96 | +SCNRScheduleWithRunLoop.argtypes = [c_void_p, |
97 | + c_void_p, |
98 | + c_void_p] |
99 | + |
100 | +# ctypes callback type to match SCNetworkReachabilityCallback |
101 | +# void (*SCNetworkReachabilityCallback) (SCNetworkReachabilityRef, |
102 | +# SCNetworkReachabilityFlags, |
103 | +# void *) |
104 | +SCNRCallbackType = CFUNCTYPE(None, c_void_p, c_uint32, c_void_p) |
105 | +# NOTE: need to keep this reference alive as long as a callback might occur. |
106 | + |
107 | +# Boolean SCNetworkReachabilitySetCallback(SCNetworkReachabilityRef, |
108 | +# SCNetworkReachabilityCallback, |
109 | +# SCNetworkReachabilityContext) |
110 | +SCNRSetCallback = SC.SCNetworkReachabilitySetCallback |
111 | +SCNRSetCallback.restype = c_bool |
112 | +SCNRSetCallback.argtypes = [c_void_p, |
113 | + SCNRCallbackType, |
114 | + c_void_p] |
115 | +# pylint: enable=E1101 |
116 | + |
117 | + |
118 | +def check_connected_state(): |
119 | + """Calls Synchronous SCNR API, returns bool.""" |
120 | + target = SCNRCreateWithName(None, HOSTNAME_TO_CHECK) |
121 | + if target is None: |
122 | + logger.error("Error creating network reachability reference.") |
123 | + raise NetworkFailException() |
124 | + |
125 | + flags = c_uint32(0) |
126 | + ok = SCNRGetFlags(target, pointer(flags)) |
127 | + CFRelease(target) |
128 | + |
129 | + if not ok: |
130 | + logger.error("Error getting reachability status of '%s'" % \ |
131 | + HOSTNAME_TO_CHECK) |
132 | + raise NetworkFailException() |
133 | + |
134 | + return flags_say_reachable(flags.value) |
135 | + |
136 | + |
137 | +def flags_say_reachable(flags): |
138 | + """Check flags returned from SCNetworkReachability API. Returns bool. |
139 | + |
140 | + Requires some logic: |
141 | + reachable_flag isn't enough on its own. |
142 | + |
143 | + A down wifi will return flags = 7, or reachable_flag and |
144 | + connection_required_flag, meaning that the host *would be* |
145 | + reachable, but you need a connection first. (And then you'd |
146 | + presumably be best off checking again.) |
147 | + """ |
148 | + # values from SCNetworkReachability.h |
149 | + reachable_flag = 1 << 1 |
150 | + connection_required_flag = 1 << 2 |
151 | + |
152 | + if flags & connection_required_flag: |
153 | + return False |
154 | + elif flags & reachable_flag: |
155 | + return True |
156 | + else: |
157 | + return False |
158 | + |
159 | + |
160 | +class SCNRContext(Structure): |
161 | + |
162 | + """A struct to send as SCNetworkReachabilityContext to SCNRSetCallback. |
163 | + |
164 | + We don't use the fields currently. |
165 | + """ |
166 | + |
167 | + _fields_ = [("version", c_long), |
168 | + ("info", c_void_p), |
169 | + ("retain", c_void_p), # func ptr |
170 | + ("release", c_void_p), # func ptr |
171 | + ("copyDescription", c_void_p)] # func ptr |
172 | + |
173 | |
174 | class NetworkManagerState(object): |
175 | - """All likely to change very soon.""" |
176 | + |
177 | + """Probe Network State and receive callbacks on changes. |
178 | + |
179 | + This class uses both synchronous and async API from the |
180 | + SystemConfiguration framework. |
181 | + |
182 | + To use: Initialize with a callback function, then call |
183 | + find_online_state. The callback will be called once immediately |
184 | + with the current state and then only on state changes. |
185 | + |
186 | + Any exceptions in checking state will result in the callback being |
187 | + called with UNKNOWN. At this point the listening thread is no |
188 | + longer runing, and a new NetworkManagerState should be created. |
189 | + |
190 | + NOTE: the callback will be called from the separate listening |
191 | + thread, except for the first call. |
192 | + """ |
193 | |
194 | def __init__(self, result_cb): |
195 | - """Initialize this instance with a result and error callbacks.""" |
196 | + """Initialize and save result callback function. |
197 | + |
198 | + result_cb should take one argument, a networkstate object. |
199 | + |
200 | + The callback will be called with one of ONLINE, OFFLINE, or |
201 | + UNKNOWN, as defined in networkstates.py. |
202 | + """ |
203 | self.result_cb = result_cb |
204 | - self.state_signal = None |
205 | - |
206 | - def call_result_cb(self, state): |
207 | - """Return the state thru the result callback.""" |
208 | - self.result_cb(state) |
209 | - |
210 | - def got_state(self, state): |
211 | - """Not actually called by DBus when the state is retrieved from NM.""" |
212 | - if state in NM_STATE_CONNECTED_LIST: |
213 | - self.call_result_cb(ONLINE) |
214 | - elif state in NM_STATE_CONNECTING_LIST: |
215 | - logger.debug("Currently connecting, waiting for signal") |
216 | - else: |
217 | - self.call_result_cb(OFFLINE) |
218 | - |
219 | - def got_error(self, error): |
220 | - """Not actually called by DBus when the state is retrieved from NM.""" |
221 | - logger.error("Error contacting NetworkManager: %s" % \ |
222 | - str(error)) |
223 | - self.call_result_cb(UNKNOWN) |
224 | - |
225 | - def state_changed(self, state): |
226 | - """Called when a signal is emmited by Network Manager.""" |
227 | - if int(state) in NM_STATE_CONNECTED_LIST: |
228 | - self.call_result_cb(ONLINE) |
229 | - elif int(state) in NM_STATE_DISCONNECTED_LIST: |
230 | - self.call_result_cb(OFFLINE) |
231 | - else: |
232 | - logger.debug("Not yet connected: continuing to wait") |
233 | + self.listener_thread = None |
234 | + |
235 | + def _state_changed(self, flags): |
236 | + """Testable callback called by reachability_state_changed_cb. |
237 | + |
238 | + Used because reachability_state_changed_cb has to have a |
239 | + particular method signature. |
240 | + |
241 | + Clients should not call this method. |
242 | + """ |
243 | + if flags_say_reachable(flags): |
244 | + self.result_cb(ONLINE) |
245 | + else: |
246 | + self.result_cb(OFFLINE) |
247 | + |
248 | + def _listen_on_separate_thread(self): |
249 | + """In separate thread, setup callback and listen for changes. |
250 | + |
251 | + On error, calls result_cb(UNKNOWN) and returns. |
252 | + """ |
253 | + |
254 | + def reachability_state_changed_cb(targetref, flags, info): |
255 | + """Callback for SCNetworkReachability API |
256 | + |
257 | + This callback is passed to the SCNetworkReachability API, |
258 | + so its method signature has to be exactly this. Therefore, |
259 | + we declare it here and just call _state_changed with |
260 | + flags.""" |
261 | + self._state_changed(flags) |
262 | + |
263 | + c_callback = SCNRCallbackType(reachability_state_changed_cb) |
264 | + context = SCNRContext(0, None, None, None, None) |
265 | + |
266 | + target = SCNRCreateWithName(None, HOSTNAME_TO_CHECK) |
267 | + if target is None: |
268 | + logger.error("Error creating SCNetworkReachability target") |
269 | + self.result_cb(UNKNOWN) |
270 | + return |
271 | + |
272 | + ok = SCNRSetCallback(target, c_callback, pointer(context)) |
273 | + if not ok: |
274 | + logger.error("error setting SCNetworkReachability callback") |
275 | + CFRelease(target) |
276 | + self.result_cb(UNKNOWN) |
277 | + return |
278 | + |
279 | + ok = SCNRScheduleWithRunLoop(target, |
280 | + CFRunLoopGetCurrent(), |
281 | + kCFRunLoopDefaultMode) |
282 | + if not ok: |
283 | + logger.error("error scheduling on runloop: SCNetworkReachability") |
284 | + CFRelease(target) |
285 | + self.result_cb(UNKNOWN) |
286 | + return |
287 | + |
288 | + CFRunLoopRun() |
289 | + |
290 | + CFRelease(target) # won't happen |
291 | + |
292 | + def _start_listening_thread(self): |
293 | + """Start the separate listener thread. |
294 | + |
295 | + Currently will not start one more than once. |
296 | + Should be OK because we don't expect errors the listen method. |
297 | + |
298 | + To add more error handling support, you could either add a |
299 | + call to join and re-start, or client could just create a new |
300 | + NetworkManagerState. |
301 | + """ |
302 | + |
303 | + if self.listener_thread is None: |
304 | + self.listener_thread = Thread( |
305 | + target=self._listen_on_separate_thread, |
306 | + name="Ubuntu SSO Network Connection Monitor") |
307 | + self.listener_thread.daemon = True |
308 | + self.listener_thread.start() |
309 | |
310 | def find_online_state(self): |
311 | - """Get the network state and return it thru the set callback.""" |
312 | + """Calls callback with current state. Starts listening thread.""" |
313 | try: |
314 | - self.state_changed(NM_STATE_CONNECTED_GLOBAL) |
315 | - except Exception, e: # pylint: disable=W0703 |
316 | - self.got_error(e) |
317 | + if check_connected_state(): |
318 | + self.result_cb(ONLINE) |
319 | + else: |
320 | + self.result_cb(OFFLINE) |
321 | + |
322 | + except Exception: # pylint: disable=W0703 |
323 | + logger.exception("Getting state from SCNetworkReachability") |
324 | + self.result_cb(UNKNOWN) |
325 | + return # don't start thread on error |
326 | + |
327 | + self._start_listening_thread() |
328 | |
329 | |
330 | def is_machine_connected(): |
331 | - """Return a deferred that when fired, returns if the machine is online.""" |
332 | - d = defer.Deferred() |
333 | - |
334 | - def got_state(state): |
335 | - """The state was retrieved from the Network Manager.""" |
336 | - result = int(state) in NM_STATE_CONNECTED_LIST |
337 | - d.callback(result) |
338 | - |
339 | - # pylint: disable=W0702, W0703 |
340 | + """Return a deferred that when fired, returns online state as a bool. |
341 | + |
342 | + Raises NetworkFailException for errors. |
343 | + """ |
344 | try: |
345 | - network = NetworkManagerState(got_state) |
346 | - network.find_online_state() |
347 | - except Exception, e: |
348 | - logger.exception('is_machine_connected failed with:') |
349 | - d.errback(NetworkFailException(e)) |
350 | - # pylint: enable=W0702, W0703 |
351 | - |
352 | - return d |
353 | + return defer.succeed(check_connected_state()) |
354 | + except Exception, e: # pylint: disable=W0703 |
355 | + logger.exception("Exception calling check_connected_state:") |
356 | + return defer.fail(NetworkFailException(e)) |
357 | |
358 | === modified file 'ubuntu_sso/networkstate/networkstates.py' |
359 | --- ubuntu_sso/networkstate/networkstates.py 2012-04-23 12:22:03 +0000 |
360 | +++ ubuntu_sso/networkstate/networkstates.py 2012-05-21 14:51:18 +0000 |
361 | @@ -15,14 +15,20 @@ |
362 | # with this program. If not, see <http://www.gnu.org/licenses/>. |
363 | """Network states.""" |
364 | |
365 | + |
366 | +class NetworkState(object): |
367 | + """ A simple class to add a label and make debugging easier. """ |
368 | + |
369 | + def __init__(self, label="unlabeled"): |
370 | + self.label = label |
371 | + |
372 | + def __repr__(self): |
373 | + return "Network state (%s)" % self.label |
374 | + |
375 | # Values returned by the callback |
376 | -ONLINE, OFFLINE, UNKNOWN = object(), object(), object() |
377 | - |
378 | -NM_STATE_NAMES = { |
379 | - ONLINE: "online", |
380 | - OFFLINE: "offline", |
381 | - UNKNOWN: "unknown", |
382 | -} |
383 | +(ONLINE, OFFLINE, UNKNOWN) = (NetworkState("online"), |
384 | + NetworkState("offline"), |
385 | + NetworkState("unknown")) |
386 | |
387 | # Internal NetworkManager State constants |
388 | NM_STATE_UNKNOWN = 0 |
389 | |
390 | === added file 'ubuntu_sso/networkstate/tests/run_nwmgr_standalone.py' |
391 | --- ubuntu_sso/networkstate/tests/run_nwmgr_standalone.py 1970-01-01 00:00:00 +0000 |
392 | +++ ubuntu_sso/networkstate/tests/run_nwmgr_standalone.py 2012-05-21 14:51:18 +0000 |
393 | @@ -0,0 +1,39 @@ |
394 | +# -*- coding: utf-8 -*- |
395 | +# |
396 | +# Copyright 2012 Canonical Ltd. |
397 | +# |
398 | +# This program is free software: you can redistribute it and/or modify it |
399 | +# under the terms of the GNU General Public License version 3, as published |
400 | +# by the Free Software Foundation. |
401 | +# |
402 | +# This program is distributed in the hope that it will be useful, but |
403 | +# WITHOUT ANY WARRANTY; without even the implied warranties of |
404 | +# MERCHANTABILITY, SATISFACTORY QUALITY, or FITNESS FOR A PARTICULAR |
405 | +# PURPOSE. See the GNU General Public License for more details. |
406 | +# |
407 | +# You should have received a copy of the GNU General Public License along |
408 | +# with this program. If not, see <http://www.gnu.org/licenses/>. |
409 | +""" |
410 | +A test script to start a NetworkManagerState instance and print out |
411 | +the current actual state of the system. |
412 | +""" |
413 | + |
414 | + |
415 | +from ubuntu_sso.networkstate import (NetworkManagerState, |
416 | + is_machine_connected) |
417 | + |
418 | + |
419 | +def my_cb(state): |
420 | + """simple callback just prints state""" |
421 | + print "one.ubuntu.com:", state |
422 | + |
423 | +if __name__ == '__main__': |
424 | + # test once with direct call: |
425 | + is_machine_connected().addCallback(my_cb) |
426 | + |
427 | + NMS = NetworkManagerState(my_cb) |
428 | + |
429 | + NMS.find_online_state() |
430 | + |
431 | + import time |
432 | + time.sleep(60) |
433 | |
434 | === added file 'ubuntu_sso/networkstate/tests/test_darwin.py' |
435 | --- ubuntu_sso/networkstate/tests/test_darwin.py 1970-01-01 00:00:00 +0000 |
436 | +++ ubuntu_sso/networkstate/tests/test_darwin.py 2012-05-21 14:51:18 +0000 |
437 | @@ -0,0 +1,193 @@ |
438 | +# -*- coding: utf-8 -*- |
439 | +# |
440 | +# Copyright 2010-2012 Canonical Ltd. |
441 | +# |
442 | +# This program is free software: you can redistribute it and/or modify it |
443 | +# under the terms of the GNU General Public License version 3, as published |
444 | +# by the Free Software Foundation. |
445 | +# |
446 | +# This program is distributed in the hope that it will be useful, but |
447 | +# WITHOUT ANY WARRANTY; without even the implied warranties of |
448 | +# MERCHANTABILITY, SATISFACTORY QUALITY, or FITNESS FOR A PARTICULAR |
449 | +# PURPOSE. See the GNU General Public License for more details. |
450 | +# |
451 | +# You should have received a copy of the GNU General Public License along |
452 | +# with this program. If not, see <http://www.gnu.org/licenses/>. |
453 | +# |
454 | +# In addition, as a special exception, the copyright holders give |
455 | +# permission to link the code of portions of this program with the |
456 | +# OpenSSL library under certain conditions as described in each |
457 | +# individual source file, and distribute linked combinations |
458 | +# including the two. |
459 | +# You must obey the GNU General Public License in all respects |
460 | +# for all of the code used other than OpenSSL. If you modify |
461 | +# file(s) with this exception, you may extend this exception to your |
462 | +# version of the file(s), but you are not obligated to do so. If you |
463 | +# do not wish to do so, delete this exception statement from your |
464 | +# version. If you delete this exception statement from all source |
465 | +# files in the program, then also delete it here. |
466 | +"""Tests for the network state detection code.""" |
467 | + |
468 | +from twisted.internet.defer import inlineCallbacks |
469 | + |
470 | +from ubuntu_sso.tests import TestCase |
471 | +from ubuntu_sso.networkstate import ( |
472 | + darwin, |
473 | + NetworkFailException, |
474 | + ) |
475 | + |
476 | +from ubuntu_sso.networkstate.networkstates import ( |
477 | + ONLINE, OFFLINE, UNKNOWN, |
478 | + ) |
479 | + |
480 | +from ubuntu_sso.networkstate.darwin import ( |
481 | + is_machine_connected, |
482 | + NetworkManagerState |
483 | + ) |
484 | + |
485 | +from ubuntu_sso.networkstate.darwin import flags_say_reachable |
486 | + |
487 | + |
488 | +REACHABLE_FLAG = 1 << 1 |
489 | +CONNECTION_REQUIRED_FLAG = 1 << 2 |
490 | + |
491 | + |
492 | +class TestSCNRFailingInDirect(TestCase): |
493 | + |
494 | + """Test that we handle a problem getting status in a direct call |
495 | + to check_connected_state (used by is_machine_connected) by |
496 | + raising an exception. |
497 | + """ |
498 | + |
499 | + def test_cant_create_target(self): |
500 | + """SCNRCreateWithName returning None should cause an exception.""" |
501 | + self.patch(darwin, "SCNRCreateWithName", |
502 | + lambda _1, _2: None) |
503 | + # pylint: disable=W0212 |
504 | + self.assertRaises(NetworkFailException, |
505 | + darwin.check_connected_state) |
506 | + |
507 | + def test_cant_get_flags(self): |
508 | + """SCNRGetFlags returning False should cause an exception.""" |
509 | + self.patch(darwin, "SCNRGetFlags", |
510 | + lambda _1, _2: False) |
511 | + # pylint: disable=W0212 |
512 | + self.assertRaises(NetworkFailException, |
513 | + darwin.check_connected_state) |
514 | + |
515 | + |
516 | +class TestFailingSCNRInCallbacks(TestCase): |
517 | + |
518 | + """Test that we handle a problem getting status in the separate |
519 | + listening thread by updating the status to UNKNOWN. |
520 | + """ |
521 | + |
522 | + def expect_unknown(self, state): |
523 | + """A convenience callback that fails unless it sees UNKNOWN.""" |
524 | + self.assertEquals(state, UNKNOWN) |
525 | + |
526 | + def test_exc_in_find_online_state(self): |
527 | + """Expect UNKNOWN from find_online_state in case of exception.""" |
528 | + def fake_check_connected_state(): |
529 | + "fake a broken check_connected_state" |
530 | + raise NetworkFailException() |
531 | + |
532 | + self.patch(darwin, "check_connected_state", |
533 | + fake_check_connected_state) |
534 | + NetworkManagerState(self.expect_unknown) |
535 | + |
536 | + def test_cant_create_target(self): |
537 | + """SCNRCreateWithName returning None -> callback gets UNKNOWN.""" |
538 | + self.patch(darwin, "SCNRCreateWithName", lambda _1, _2: None) |
539 | + nms = NetworkManagerState(self.expect_unknown) |
540 | + # pylint: disable=W0212 |
541 | + nms._listen_on_separate_thread() |
542 | + |
543 | + def test_cant_set_callback(self): |
544 | + """SCNRSetCallback returning false -> callback gets UNKNOWN.""" |
545 | + self.patch(darwin, "SCNRSetCallback", lambda _1, _2, _3: False) |
546 | + nms = NetworkManagerState(self.expect_unknown) |
547 | + # pylint: disable=W0212 |
548 | + nms._listen_on_separate_thread() |
549 | + |
550 | + def test_cant_schedule_with_runloop(self): |
551 | + """SCNRScheduleWithRunLoop returning false -> callback gets UNKNOWN.""" |
552 | + self.patch(darwin, "SCNRScheduleWithRunLoop", |
553 | + lambda _1, _2, _3: False) |
554 | + nms = NetworkManagerState(self.expect_unknown) |
555 | + # pylint: disable=W0212 |
556 | + nms._listen_on_separate_thread() |
557 | + |
558 | + |
559 | +class TestReadingFlags(TestCase): |
560 | + """Test interpretation of flags returned from SCNR API""" |
561 | + |
562 | + def test_flag_reachable(self): |
563 | + """Reachable by itself is OK.""" |
564 | + flag = REACHABLE_FLAG |
565 | + self.assertTrue(flags_say_reachable(flag)) |
566 | + |
567 | + def test_flag_reachable_and_flag_connection_required(self): |
568 | + """Reachable and connection-required is NOT OK""" |
569 | + flag = REACHABLE_FLAG | CONNECTION_REQUIRED_FLAG |
570 | + self.assertFalse(flags_say_reachable(flag)) |
571 | + |
572 | + def test_other_flagvals(self): |
573 | + """All other flag configurations are false for our purposes. |
574 | + |
575 | + They either indicate an iOS device, which we won't run this |
576 | + code on, or that the server we're testing for is on this |
577 | + machine or wired directly to it. These cases won't happen. |
578 | + """ |
579 | + for flag in range(0, 17) + [1 << 16, 1 << 17, 1 << 18]: |
580 | + # only test cases without the reachable bit set: |
581 | + flag = flag & ~ 2 |
582 | + self.assertEqual(False, flags_say_reachable(flag)) |
583 | + |
584 | + |
585 | +class TestNMSListeningForNWStateChanges(TestCase): |
586 | + """ |
587 | + Test that the NetworkManagerState class calls the callback with |
588 | + ONLINE/OFFLINE when the state changes appropriately |
589 | + """ |
590 | + |
591 | + @inlineCallbacks |
592 | + def setUp(self): |
593 | + """Setup array to hold state changes.""" |
594 | + yield super(TestNMSListeningForNWStateChanges, self).setUp() |
595 | + self.network_changes = [] |
596 | + |
597 | + def _listen_network_changes(self, state): |
598 | + """Fake callback function, records state changes.""" |
599 | + self.network_changes.append(state) |
600 | + |
601 | + def test_network_state_change(self): |
602 | + """Test the changes in the network connection.""" |
603 | + nms = NetworkManagerState(self._listen_network_changes) |
604 | + # pylint: disable=W0212 |
605 | + nms._state_changed(2) |
606 | + nms._state_changed(0) # 0 or anything other than 2. |
607 | + nms._state_changed(2) |
608 | + |
609 | + self.assertEqual(self.network_changes, |
610 | + [ONLINE, OFFLINE, ONLINE]) |
611 | + |
612 | + |
613 | +class TestIsMachineConnectedFunc(TestCase): |
614 | + """Simple test of is_machine_connected.""" |
615 | + |
616 | + @inlineCallbacks |
617 | + def test_not_connected_returns_false(self): |
618 | + """test that False comes back False""" |
619 | + self.patch(darwin, "check_connected_state", |
620 | + lambda: False) |
621 | + con = yield is_machine_connected() |
622 | + self.assertEqual(con, False) |
623 | + |
624 | + @inlineCallbacks |
625 | + def test_connected_returns_true(self): |
626 | + """check that True comes back True""" |
627 | + self.patch(darwin, "check_connected_state", |
628 | + lambda: True) |
629 | + con = yield is_machine_connected() |
630 | + self.assertEqual(con, True) |
When trying to run the tests on linux I get the following import error:
*** Running GTK test suite for ubuntu_sso *** bin/u1trial" , line 325, in <module> bin/u1trial" , line 305, in main get_suite( config) bin/u1trial" , line 184, in get_suite 'ignore- paths'] )) bin/u1trial" , line 168, in _collect_tests unittest( filepath) bin/u1trial" , line 108, in _load_unittest mandel/ Projects/ Canonical/ ubuntu- sso-client/ fix-networkstat e-darwin/ ubuntu_ sso/networkstat e/tests/ test_darwin. py", line 37, in <module> sso.networkstat e import ( mandel/ Projects/ Canonical/ ubuntu- sso-client/ fix-networkstat e-darwin/ ubuntu_ sso/networkstat e/darwin. py", line 55, in <module> tCurrent = CoreFoundation. CFRunLoopGetCur rent python2. 7/ctypes/ __init_ _.py", line 378, in __getattr__ getitem_ _(name) python2. 7/ctypes/ __init_ _.py", line 383, in __getitem__ (name_or_ ordinal, self))
Traceback (most recent call last):
File "/usr/local/
main()
File "/usr/local/
suite = trial_runner.
File "/usr/local/
config[
File "/usr/local/
module_suite = self._load_
File "/usr/local/
module = __import__(modpath, None, None, [""])
File "/home/
from ubuntu_
File "/home/
CFRunLoopGe
File "/usr/lib/
func = self.__
File "/usr/lib/
func = self._FuncPtr(
AttributeError: /usr/bin/python: undefined symbol: CFRunLoopGetCurrent
Probably there is a missing module in the list of ingored modules