Merge lp:~mikemc/ubuntu-sso-client/fix-networkstate-darwin into lp:ubuntu-sso-client

Proposed by Mike McCracken
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
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/networkstates.py.

2. The is_machine_connected() function, which returns a deferred that
calls the synchronous API to check the current connectivity once.

This branch includes two other changes:

1. run-nwmgr-standalone.py is a utility for testing this API live, but in
isolation. It should be platform-independent. Just run it and it will
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-in-linuxnwindows-tests branch because it introduces the test_darwin.py script that that branch guards against. Without that branch, this branch doesn't test well.

To post a comment you must log in.
Revision history for this message
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 ***
Traceback (most recent call last):
  File "/usr/local/bin/u1trial", line 325, in <module>
    main()
  File "/usr/local/bin/u1trial", line 305, in main
    suite = trial_runner.get_suite(config)
  File "/usr/local/bin/u1trial", line 184, in get_suite
    config['ignore-paths']))
  File "/usr/local/bin/u1trial", line 168, in _collect_tests
    module_suite = self._load_unittest(filepath)
  File "/usr/local/bin/u1trial", line 108, in _load_unittest
    module = __import__(modpath, None, None, [""])
  File "/home/mandel/Projects/Canonical/ubuntu-sso-client/fix-networkstate-darwin/ubuntu_sso/networkstate/tests/test_darwin.py", line 37, in <module>
    from ubuntu_sso.networkstate import (
  File "/home/mandel/Projects/Canonical/ubuntu-sso-client/fix-networkstate-darwin/ubuntu_sso/networkstate/darwin.py", line 55, in <module>
    CFRunLoopGetCurrent = CoreFoundation.CFRunLoopGetCurrent
  File "/usr/lib/python2.7/ctypes/__init__.py", line 378, in __getattr__
    func = self.__getitem__(name)
  File "/usr/lib/python2.7/ctypes/__init__.py", line 383, in __getitem__
    func = self._FuncPtr((name_or_ordinal, self))
AttributeError: /usr/bin/python: undefined symbol: CFRunLoopGetCurrent

Probably there is a missing module in the list of ingored modules

review: Needs Fixing
970. By Mike McCracken

merge with linux and windows test script branch so you can test this branch on linux and windows

Revision history for this message
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/mandel/Projects/Canonical/ubuntu-sso-client/fix-networkstate-
> darwin/ubuntu_sso/networkstate/darwin.py", line 55, in <module>
> CFRunLoopGetCurrent = CoreFoundation.CFRunLoopGetCurrent
> File "/usr/lib/python2.7/ctypes/__init__.py", line 378, in __getattr__
> func = self.__getitem__(name)
> File "/usr/lib/python2.7/ctypes/__init__.py", line 383, in __getitem__
> func = self._FuncPtr((name_or_ordinal, self))
> 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...

Revision history for this message
Manuel de la Peña (mandel) wrote :
Download full text (6.5 KiB)

> > When trying to run the tests on linux I get the following import error:
> >
> > *** Running GTK test suite for ubuntu_sso ***
>
> ...snip...
>
> > File "/home/mandel/Projects/Canonical/ubuntu-sso-client/fix-networkstate-
> > darwin/ubuntu_sso/networkstate/darwin.py", line 55, in <module>
> > CFRunLoopGetCurrent = CoreFoundation.CFRunLoopGetCurrent
> > File "/usr/lib/python2.7/ctypes/__init__.py", line 378, in __getattr__
> > func = self.__getitem__(name)
> > File "/usr/lib/python2.7/ctypes/__init__.py", line 383, in __getitem__
> > func = self._FuncPtr((name_or_ordinal, self))
> > 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_sso/networkstate/darwin.py:
    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 "CoreFoundationPath" (should match (([A-Z_][A-Z0-9_]*)|(__.*__))$)
    52: [C0103] Invalid name "CoreFoundation" (should match (([A-Z_][A-Z0-9_]*)|(__.*__))$)
    55: [C0103] Invalid name "CFRunLoopGetCurrent" (should match (([A-Z_][A-Z0-9_]*)|(__.*__))$)
    60: [C0103] Invalid name "CFRelease" (should match (([A-Z_][A-Z0-9_]*)|(__.*__))$)
    65: [C0103] Invalid name "CFRunLoopRun" (should match (([A-Z_][A-Z0-9_]*)|(__.*__))$)
    68: [C0103] Invalid name "kCFRunLoopDefaultMode" (should match (([A-Z_][A-Z0-9_]*)|(__.*__))$)
    68: [E1101] Class 'c_void_p' has no 'in_dll' member
    73: [C0103] Invalid name "SystemConfigurationPath" (should match (([A-Z_][A-Z0-9_]*)|(__.*__))$)
    82: [C0103] Invalid name "SCNRCreateWithName" (should match (([A-Z_][A-Z0-9_]*)|(__.*__))$)
    87: [C0103] Invalid name "SCNRGetFlags" (should match (([A-Z_][A-Z0-9_]*)|(__.*__))$)
    92: [C0103] Invalid name "SCNRScheduleWithRunLoop" (should match (([A-Z_][A-Z0-9_]*)|(__.*__))$)
    102: [C0103] Invalid name "SCNRCallbackType" (should match (([A-Z_][A-Z0-9_]*)|(__.*__))$)
    108: [C0103] Invalid name "SCNRSetCallback" (should match (([A-Z_][A-Z0-9_]*)|(__.*__))$)
    115: [C0111, _check_connected_state] Missing docstring
    145: [C0103, flags_say_reachable] Invalid name "kReachable" (should match [a-z_][a-z0-9_]{1,30}$)
    146: [C0103, flags_say_reachable] Invalid name "kConnectionRequired" (should match [a-z_][a-z0-9_]{1,30}$)
    270: [W0612, NetworkManagerState.find_online_state] Unused variable 'e'
    282: [W0703, is_machine_connected] Catching too general exception Exception
    23: [W0611] Unused import NM_STATE_DISCONNECTED_LIST
    30: [W0611] Unused import NM_STATE_CONNECTED_GLOBAL
    23: [W0611] Unused import NM_STATE_CONNECTED_LIST
    23: [W0611] Unused import NM_STATE_CONNECTING_LIST

ubuntu_sso/networkstate/networkstates.py:
    18: [C0111, NetworkState] Missing docstrin...

Read more...

review: Needs Fixing
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

Revision history for this message
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_state() == True => if check_connected_state():
* self.assertEqual(True, flags_say_reachable(flag)) => self.assertTrue(flags_say_reachable(flag))
* self.assertEqual(False, flags_say_reachable(flag)) => self.assertFalse(flags_say_reachable(flag))
* from ubuntu_sso.networkstate.darwin import flags_say_reachable => Can this be imported at the top of the file?

Tests and lint checks pass, please let me know when I can take another look.

review: Needs Fixing
975. By Mike McCracken

style fixes

Revision history for this message
Manuel de la Peña (mandel) wrote :

Looks good.

review: Approve
Revision history for this message
Diego Sarmentero (diegosarmentero) wrote :

+1

review: Approve

Preview Diff

[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
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)

Subscribers

People subscribed via source and target branches