Merge lp:~dobey/ubuntu-sso-client/update-stable-4-0 into lp:ubuntu-sso-client/stable-4-0

Proposed by dobey on 2012-06-05
Status: Merged
Approved by: dobey on 2012-06-05
Approved revision: 958
Merged at revision: 958
Proposed branch: lp:~dobey/ubuntu-sso-client/update-stable-4-0
Merge into: lp:ubuntu-sso-client/stable-4-0
Diff against target: 1068 lines (+576/-277)
12 files modified
data/qt/reset_password.ui (+3/-3)
run-tests (+7/-3)
run-tests.bat (+1/-1)
setup.py (+2/-53)
ubuntu_sso/account.py (+0/-23)
ubuntu_sso/networkstate/darwin.py (+269/-61)
ubuntu_sso/networkstate/linux.py (+4/-1)
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)
ubuntu_sso/networkstate/tests/test_linux.py (+45/-93)
ubuntu_sso/tests/test_account.py (+0/-32)
To merge this branch: bzr merge lp:~dobey/ubuntu-sso-client/update-stable-4-0
Reviewer Review Type Date Requested Status
Mike McCracken (community) Approve on 2012-06-05
Eric Casteleijn (community) 2012-06-05 Approve on 2012-06-05
Review via email: mp+108754@code.launchpad.net

Commit Message

[Brian Curtin]

    Cleanup setup.py of lazr imports and lazr-specific features.

[György Balló.]

    Removed the lazr.restfulclient dependency by removing TimestampedAuthorizer class.

[Mike McCracken]

    Fixes "TypeError in got_state()" message and incorrect network detection on linux (LP: #1003692 )
    Implement internet connectivity detection in Mac OS X ("darwin") systems (LP: #984667 )
    Fix test scripts for linux and windows to avoid running darwin tests

[Diego Sarmentero]

    Setting the size property to expanding and adding a maximum width (LP: #999885).

To post a comment you must log in.
Eric Casteleijn (thisfred) :
review: Approve
Mike McCracken (mikemc) :
review: Approve
Ubuntu One Auto Pilot (otto-pilot) wrote :

The attempt to merge lp:~dobey/ubuntu-sso-client/update-stable-4-0 into lp:ubuntu-sso-client/stable-4-0 failed. Below is the output from the failed tests.

Ubuntu One Auto Pilot (otto-pilot) wrote :

The attempt to merge lp:~dobey/ubuntu-sso-client/update-stable-4-0 into lp:ubuntu-sso-client/stable-4-0 failed. Below is the output from the failed tests.

Ubuntu One Auto Pilot (otto-pilot) wrote :

The attempt to merge lp:~dobey/ubuntu-sso-client/update-stable-4-0 into lp:ubuntu-sso-client/stable-4-0 failed. Below is the output from the failed tests.

*** Running GTK test suite for ubuntu_sso ***
/usr/bin/xvfb-run: 182: /usr/bin/xvfb-run: u1trial: not found

Ubuntu One Auto Pilot (otto-pilot) wrote :

The attempt to merge lp:~dobey/ubuntu-sso-client/update-stable-4-0 into lp:ubuntu-sso-client/stable-4-0 failed. Below is the output from the failed tests.

*** Running GTK test suite for ubuntu_sso ***
Traceback (most recent call last):
  File "/usr/bin/u1trial", line 337, in <module>
    main()
  File "/usr/bin/u1trial", line 317, in main
    suite = trial_runner.get_suite(config)
  File "/usr/bin/u1trial", line 196, in get_suite
    config['ignore-paths']))
  File "/usr/bin/u1trial", line 180, in _collect_tests
    module_suite = self._load_unittest(filepath)
  File "/usr/bin/u1trial", line 120, in _load_unittest
    module = __import__(modpath, None, None, [""])
  File "/mnt/tarmac/cache/ubuntu-sso-client/stable-3-0/ubuntu_sso/networkstate/tests/test_linux.py", line 34, in <module>
    from mocker import ARGS, KWARGS, ANY, MockerTestCase
ImportError: No module named mocker

Ubuntu One Auto Pilot (otto-pilot) wrote :
Download full text (185.5 KiB)

The attempt to merge lp:~dobey/ubuntu-sso-client/update-stable-4-0 into lp:ubuntu-sso-client/stable-4-0 failed. Below is the output from the failed tests.

*** Running GTK test suite for ubuntu_sso ***
ubuntu_sso.tests.test_account
  AccountTestCase
    test_generate_captcha ... [OK]
    test_is_not_validated ... [OK]
    test_is_not_validated_empty_result ... [OK]
    test_is_validated ... [OK]
    test_login_if_http_error ... [OK]
    test_login_if_no_error ... [OK]
    test_register_user_checks_valid_email ... [OK]
    test_register_user_checks_valid_password ... [OK]
    test_register_user_if_status_error ... [OK]
    test_register_user_if_status_error_with_string_message ... [OK]
    test_register_user_if_status_ok ... [OK]
    test_register_user_if_status_unknown ... [OK]
    test_request_password_reset_token_if_http_error ... [OK]
    test_request_password_reset_token_if_status_ok ... [OK]
    test_request_password_reset_token_if_status_unknown ... [OK]
    test_set_new_password_if_http_error ... [OK]
    test_set_new_password_if_status_ok ... [OK]
    test_set_new_password_if_status_unknown ... [OK]
    test_validate_email_if_status_error ... [OK]
    test_validate_email_if_status_error_with_string_message ... [OK]
    test_validate_email_if_status_ok ... [OK]
    test_validate_email_if_status_unknown ... [OK]
  EnvironOverridesTestCase
    test_no_override_service_url ... [OK]
    test_override_service_url ... [OK]
    test_service_url_as_parameter ... [OK]
twisted.trial.unittest
  TestCase
    runTest ... [OK]
ubuntu_sso.tests.test_credentials
  BasicTestCase
    runTest ... [OK]
  ClearCredentialsTestCase
    test_clear_credentials ... [OK]
    test_keyring_failure ... [OK]
  CredentialsCallbacksTestCase
    test_creation_parameters_are_stored ... [OK]
    test_help_text_defaults_to_empty_string ... [OK]
    test_ping_url_defaults_to_none ... [OK]
    test_policy_url_defaults_to_none ... [OK]
    test_tc_url_defaults_to_none ... [OK]
    test_ui_executable_defaults_to_gtk ... ...

Preview Diff

[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
1=== modified file 'data/qt/reset_password.ui'
2--- data/qt/reset_password.ui 2012-03-16 21:03:09 +0000
3+++ data/qt/reset_password.ui 2012-06-05 14:23:23 +0000
4@@ -202,7 +202,7 @@
5 <item row="1" column="1" rowspan="2">
6 <widget class="QLabel" name="password_assistance">
7 <property name="sizePolicy">
8- <sizepolicy hsizetype="Preferred" vsizetype="Preferred">
9+ <sizepolicy hsizetype="Expanding" vsizetype="Expanding">
10 <horstretch>0</horstretch>
11 <verstretch>0</verstretch>
12 </sizepolicy>
13@@ -215,8 +215,8 @@
14 </property>
15 <property name="maximumSize">
16 <size>
17- <width>185</width>
18- <height>95</height>
19+ <width>300</width>
20+ <height>16777215</height>
21 </size>
22 </property>
23 <property name="text">
24
25=== modified file 'run-tests'
26--- run-tests 2012-04-20 19:28:22 +0000
27+++ run-tests 2012-06-05 14:23:23 +0000
28@@ -28,9 +28,13 @@
29 # do not wish to do so, delete this exception statement from your
30 # version. If you delete this exception statement from all source
31 # files in the program, then also delete it here.
32+
33 QT_TESTS_PATH="ubuntu_sso/qt/tests, ubuntu_sso/qt/main/tests"
34+QT_TEST_FILES="test_qt.py,test_qtwisted.py"
35+
36 GTK_TESTS_PATH=ubuntu_sso/gtk/tests
37-WINDOWS_TESTS=test_windows.py
38+
39+LINUX_IGNORE_FILES="test_darwin.py,test_windows.py,test_pykeyring.py"
40
41 set -e
42
43@@ -59,7 +63,7 @@
44 fi
45
46 echo "*** Running GTK test suite for ""$MODULE"" ***"
47-$XVFB_CMDLINE u1trial --reactor=gi --gui -p "$QT_TESTS_PATH" -i "test_windows.py,test_qt.py,test_qtwisted.py,test_pykeyring.py" "$MODULE"
48+$XVFB_CMDLINE u1trial --reactor=gi --gui -p "$QT_TESTS_PATH" -i "$QT_TEST_FILES,$LINUX_IGNORE_FILES" "$MODULE"
49 rm -rf _trial_temp
50
51 # hack to avoid:
52@@ -69,7 +73,7 @@
53
54 echo "*** Running QT test suite for ""$MODULE"" ***"
55 ./setup.py build
56-$XVFB_CMDLINE u1trial --reactor=qt4 --gui -p "$GTK_TESTS_PATH" -i "test_windows.py" "$MODULE"
57+$XVFB_CMDLINE u1trial --reactor=qt4 --gui -p "$GTK_TESTS_PATH" -i $LINUX_IGNORE_FILES "$MODULE"
58 rm -rf _trial_temp
59 rm -rf build
60
61
62=== modified file 'run-tests.bat'
63--- run-tests.bat 2012-04-20 18:43:44 +0000
64+++ run-tests.bat 2012-06-05 14:23:23 +0000
65@@ -74,7 +74,7 @@
66 "%PYTHONEXEPATH%" setup.py build
67 ECHO Running tests
68 :: execute the tests with a number of ignored linux only modules
69-"%PYTHONEXEPATH%" "%TRIALPATH%" -i "test_linux.py, test_txsecrets.py, test_qt.py, test_glib.py" -p "ubuntu_sso\gtk" --reactor=qt4 --gui %PARAMS% ubuntu_sso
70+"%PYTHONEXEPATH%" "%TRIALPATH%" -i "test_darwin.py, test_linux.py, test_txsecrets.py, test_qt.py, test_glib.py" -p "ubuntu_sso\gtk" --reactor=qt4 --gui %PARAMS% ubuntu_sso
71 :: Clean the build from the setupt.py
72 ECHO Cleaning the generated code before running the style checks...
73 "%PYTHONEXEPATH%" setup.py clean
74
75=== modified file 'setup.py'
76--- setup.py 2012-04-09 17:38:24 +0000
77+++ setup.py 2012-06-05 14:23:23 +0000
78@@ -201,9 +201,8 @@
79
80 def set_py2exe_paths():
81 """Set the path so that py2exe finds the required modules."""
82- # Pylint does not understand same spaced imports which is what lazr uses
83+ # Pylint does not understand same spaced imports
84 # pylint: disable=F0401
85- import lazr
86 import win32com
87 # pylint: enable=F0401
88 try:
89@@ -226,55 +225,6 @@
90 for module_path in module.__path__[1:]:
91 modulefinder.AddPackagePath(extra_mod, module_path)
92
93- # lazr uses namespaces packages, which does add some problems to py2exe
94- # the following is a way to work arround the issue
95- for path in lazr.__path__:
96- modulefinder.AddPackagePath(__name__, path)
97-
98-
99-def get_py2exe_extension():
100- """Return an extension class of py2exe."""
101- import glob
102- # pylint: disable=F0401
103- from py2exe.build_exe import py2exe as build_exe
104- # pylint: enable=F0401
105-
106- # pylint: disable=E1101,W0232
107- class MediaCollector(build_exe):
108- """Extension that copies lazr missing data."""
109-
110- def _add_module_data(self, module_name):
111- """Add the data from a given path."""
112- # Create the media subdir where the
113- # Python files are collected.
114- media = module_name.replace('.', os.path.sep)
115- full = os.path.join(self.collect_dir, media)
116- if not os.path.exists(full):
117- self.mkpath(full)
118-
119- # Copy the media files to the collection dir.
120- # Also add the copied file to the list of compiled
121- # files so it will be included in zipfile.
122- module = __import__(module_name, None, None, [''])
123- for path in module.__path__:
124- for f in glob.glob(path + '/*'): # does not like os.path.sep
125- log.info('Copying file %s', f)
126- name = os.path.basename(f)
127- if not os.path.isdir(f):
128- self.copy_file(f, os.path.join(full, name))
129- self.compiled_files.append(os.path.join(media, name))
130- else:
131- self.copy_tree(f, os.path.join(full, name))
132-
133- def copy_extensions(self, extensions):
134- """Copy the missing extensions."""
135- build_exe.copy_extensions(self, extensions)
136- for module in ['lazr.uri', 'lazr.restfulclient',
137- 'lazr.authentication', 'wadllib']:
138- self._add_module_data(module)
139- # pylint: enable=E1101
140-
141- return MediaCollector
142
143 # pylint: disable=C0103
144
145@@ -316,7 +266,6 @@
146 """Provide the required info to setup the project on windows."""
147 set_py2exe_paths()
148 _data_files = []
149- cmdclass['py2exe'] = get_py2exe_extension()
150 # for PyQt, see http://www.py2exe.org/index.cgi/Py2exeAndPyQt
151 _includes = ['sip', 'email', 'ubuntu_sso.qt.gui',
152 'ubuntu_sso.qt.controllers', 'PyQt4.QtNetwork', 'PIL']
153@@ -347,7 +296,7 @@
154
155 DistUtilsExtra.auto.setup(
156 name='ubuntu-sso-client',
157- version='3.1',
158+ version='3.99',
159 license='GPL v3',
160 author='Natalia Bidart',
161 author_email='natalia.bidart@canonical.com',
162
163=== modified file 'ubuntu_sso/account.py'
164--- ubuntu_sso/account.py 2012-04-09 17:38:24 +0000
165+++ ubuntu_sso/account.py 2012-06-05 14:23:23 +0000
166@@ -38,8 +38,6 @@
167 import os
168 import re
169
170-from lazr.restfulclient.authorize.oauth import OAuthAuthorizer
171-from oauth import oauth
172 from twisted.internet import defer
173
174 from ubuntu_sso.logger import setup_logging
175@@ -54,27 +52,6 @@
176 SSO_STATUS_ERROR = 'error'
177
178
179-class TimestampedAuthorizer(OAuthAuthorizer):
180- """Includes a custom timestamp on OAuth signatures."""
181-
182- def __init__(self, get_timestamp, *args, **kwargs):
183- """Store the get_timestamp method, and move on."""
184- super(TimestampedAuthorizer, self).__init__(*args, **kwargs)
185- self.get_timestamp = get_timestamp
186-
187- # pylint: disable=C0103
188- def authorizeRequest(self, absolute_uri, method, body, headers):
189- """Override authorizeRequest including the timestamp."""
190- parameters = {"oauth_timestamp": self.get_timestamp()}
191- oauth_request = oauth.OAuthRequest.from_consumer_and_token(
192- self.consumer, self.access_token, http_url=absolute_uri,
193- parameters=parameters)
194- oauth_request.sign_request(
195- oauth.OAuthSignatureMethod_PLAINTEXT(),
196- self.consumer, self.access_token)
197- headers.update(oauth_request.to_header(self.oauth_realm))
198-
199-
200 class InvalidEmailError(Exception):
201 """The email is not valid."""
202
203
204=== modified file 'ubuntu_sso/networkstate/darwin.py'
205--- ubuntu_sso/networkstate/darwin.py 2012-04-23 12:22:03 +0000
206+++ ubuntu_sso/networkstate/darwin.py 2012-06-05 14:23:23 +0000
207@@ -13,83 +13,291 @@
208 #
209 # You should have received a copy of the GNU General Public License along
210 # with this program. If not, see <http://www.gnu.org/licenses/>.
211-"""Implementation of network state detection."""
212+"""Network state detection on OS X.
213+
214+is_machine_connected(): (deferred) returns connected state as bool
215+NetworkManagerState: class with listening thread, calls back with state changes
216+"""
217
218 from twisted.internet import defer
219
220+from threading import Thread
221+
222 from ubuntu_sso.networkstate import NetworkFailException
223-from ubuntu_sso.networkstate.networkstates import (
224- ONLINE, OFFLINE, UNKNOWN,
225-
226- NM_STATE_CONNECTING_LIST,
227- NM_STATE_CONNECTED_LIST,
228- NM_STATE_DISCONNECTED_LIST,
229- )
230-from ubuntu_sso.networkstate.networkstates import NM_STATE_CONNECTED_GLOBAL
231+from ubuntu_sso.networkstate.networkstates import (ONLINE, OFFLINE, UNKNOWN)
232 from ubuntu_sso.logger import setup_logging
233 logger = setup_logging("ubuntu_sso.networkstate")
234
235+HOSTNAME_TO_CHECK = 'one.ubuntu.com'
236+
237+from ctypes import (
238+ CDLL,
239+ POINTER,
240+ CFUNCTYPE,
241+ Structure,
242+ pointer,
243+ c_bool,
244+ c_long,
245+ c_void_p,
246+ c_uint32)
247+
248+from ctypes.util import find_library
249+
250+# pylint: disable=C0103
251+
252+# Functions and constants below are from
253+# /System/Library/CoreFoundation.framework/
254+CoreFoundationPath = find_library("CoreFoundation")
255+CoreFoundation = CDLL(CoreFoundationPath)
256+
257+# CFRunLoopRef CFRunLoopGetCurrent()
258+CFRunLoopGetCurrent = CoreFoundation.CFRunLoopGetCurrent
259+CFRunLoopGetCurrent.restype = c_void_p
260+CFRunLoopGetCurrent.argtypes = []
261+
262+# void CFRelease(CFTypeRef)
263+CFRelease = CoreFoundation.CFRelease
264+CFRelease.restype = None
265+CFRelease.argtypes = [c_void_p]
266+
267+# void CFRunLoopRun()
268+CFRunLoopRun = CoreFoundation.CFRunLoopRun
269+
270+# const CFStringRef kCFRunLoopDefaultMode
271+# pylint: disable=E1101
272+kCFRunLoopDefaultMode = c_void_p.in_dll(CoreFoundation,
273+ "kCFRunLoopDefaultMode")
274+
275+
276+# Functions and constants below are from
277+# /System/Library/SystemConfiguration.framework/
278+SystemConfigurationPath = find_library("SystemConfiguration")
279+
280+# SystemConfiguration abbreviated as "SC" below:
281+SC = CDLL(SystemConfigurationPath)
282+
283+# "SCNetworkReachability" functions abbreviated to "SCNR*" here.
284+
285+# SCNetworkReachabilityRef
286+# SCNetworkReachabilityCreateWithName(CFAllocatorRef, const char *)
287+SCNRCreateWithName = SC.SCNetworkReachabilityCreateWithName
288+SCNRCreateWithName.restype = c_void_p
289+
290+# Boolean SCNetworkReachabilityGetFlags(SCNetworkReachabilityRef,
291+# SCNetworkReachabilityFlags)
292+SCNRGetFlags = SC.SCNetworkReachabilityGetFlags
293+SCNRGetFlags.restype = c_bool
294+SCNRGetFlags.argtypes = [c_void_p,
295+ POINTER(c_uint32)]
296+
297+SCNRScheduleWithRunLoop = SC.SCNetworkReachabilityScheduleWithRunLoop
298+SCNRScheduleWithRunLoop.restype = c_bool
299+SCNRScheduleWithRunLoop.argtypes = [c_void_p,
300+ c_void_p,
301+ c_void_p]
302+
303+# ctypes callback type to match SCNetworkReachabilityCallback
304+# void (*SCNetworkReachabilityCallback) (SCNetworkReachabilityRef,
305+# SCNetworkReachabilityFlags,
306+# void *)
307+SCNRCallbackType = CFUNCTYPE(None, c_void_p, c_uint32, c_void_p)
308+# NOTE: need to keep this reference alive as long as a callback might occur.
309+
310+# Boolean SCNetworkReachabilitySetCallback(SCNetworkReachabilityRef,
311+# SCNetworkReachabilityCallback,
312+# SCNetworkReachabilityContext)
313+SCNRSetCallback = SC.SCNetworkReachabilitySetCallback
314+SCNRSetCallback.restype = c_bool
315+SCNRSetCallback.argtypes = [c_void_p,
316+ SCNRCallbackType,
317+ c_void_p]
318+# pylint: enable=E1101
319+
320+
321+def check_connected_state():
322+ """Calls Synchronous SCNR API, returns bool."""
323+ target = SCNRCreateWithName(None, HOSTNAME_TO_CHECK)
324+ if target is None:
325+ logger.error("Error creating network reachability reference.")
326+ raise NetworkFailException()
327+
328+ flags = c_uint32(0)
329+ ok = SCNRGetFlags(target, pointer(flags))
330+ CFRelease(target)
331+
332+ if not ok:
333+ logger.error("Error getting reachability status of '%s'" % \
334+ HOSTNAME_TO_CHECK)
335+ raise NetworkFailException()
336+
337+ return flags_say_reachable(flags.value)
338+
339+
340+def flags_say_reachable(flags):
341+ """Check flags returned from SCNetworkReachability API. Returns bool.
342+
343+ Requires some logic:
344+ reachable_flag isn't enough on its own.
345+
346+ A down wifi will return flags = 7, or reachable_flag and
347+ connection_required_flag, meaning that the host *would be*
348+ reachable, but you need a connection first. (And then you'd
349+ presumably be best off checking again.)
350+ """
351+ # values from SCNetworkReachability.h
352+ reachable_flag = 1 << 1
353+ connection_required_flag = 1 << 2
354+
355+ if flags & connection_required_flag:
356+ return False
357+ elif flags & reachable_flag:
358+ return True
359+ else:
360+ return False
361+
362+
363+class SCNRContext(Structure):
364+
365+ """A struct to send as SCNetworkReachabilityContext to SCNRSetCallback.
366+
367+ We don't use the fields currently.
368+ """
369+
370+ _fields_ = [("version", c_long),
371+ ("info", c_void_p),
372+ ("retain", c_void_p), # func ptr
373+ ("release", c_void_p), # func ptr
374+ ("copyDescription", c_void_p)] # func ptr
375+
376
377 class NetworkManagerState(object):
378- """All likely to change very soon."""
379+
380+ """Probe Network State and receive callbacks on changes.
381+
382+ This class uses both synchronous and async API from the
383+ SystemConfiguration framework.
384+
385+ To use: Initialize with a callback function, then call
386+ find_online_state. The callback will be called once immediately
387+ with the current state and then only on state changes.
388+
389+ Any exceptions in checking state will result in the callback being
390+ called with UNKNOWN. At this point the listening thread is no
391+ longer runing, and a new NetworkManagerState should be created.
392+
393+ NOTE: the callback will be called from the separate listening
394+ thread, except for the first call.
395+ """
396
397 def __init__(self, result_cb):
398- """Initialize this instance with a result and error callbacks."""
399+ """Initialize and save result callback function.
400+
401+ result_cb should take one argument, a networkstate object.
402+
403+ The callback will be called with one of ONLINE, OFFLINE, or
404+ UNKNOWN, as defined in networkstates.py.
405+ """
406 self.result_cb = result_cb
407- self.state_signal = None
408-
409- def call_result_cb(self, state):
410- """Return the state thru the result callback."""
411- self.result_cb(state)
412-
413- def got_state(self, state):
414- """Not actually called by DBus when the state is retrieved from NM."""
415- if state in NM_STATE_CONNECTED_LIST:
416- self.call_result_cb(ONLINE)
417- elif state in NM_STATE_CONNECTING_LIST:
418- logger.debug("Currently connecting, waiting for signal")
419- else:
420- self.call_result_cb(OFFLINE)
421-
422- def got_error(self, error):
423- """Not actually called by DBus when the state is retrieved from NM."""
424- logger.error("Error contacting NetworkManager: %s" % \
425- str(error))
426- self.call_result_cb(UNKNOWN)
427-
428- def state_changed(self, state):
429- """Called when a signal is emmited by Network Manager."""
430- if int(state) in NM_STATE_CONNECTED_LIST:
431- self.call_result_cb(ONLINE)
432- elif int(state) in NM_STATE_DISCONNECTED_LIST:
433- self.call_result_cb(OFFLINE)
434- else:
435- logger.debug("Not yet connected: continuing to wait")
436+ self.listener_thread = None
437+
438+ def _state_changed(self, flags):
439+ """Testable callback called by reachability_state_changed_cb.
440+
441+ Used because reachability_state_changed_cb has to have a
442+ particular method signature.
443+
444+ Clients should not call this method.
445+ """
446+ if flags_say_reachable(flags):
447+ self.result_cb(ONLINE)
448+ else:
449+ self.result_cb(OFFLINE)
450+
451+ def _listen_on_separate_thread(self):
452+ """In separate thread, setup callback and listen for changes.
453+
454+ On error, calls result_cb(UNKNOWN) and returns.
455+ """
456+
457+ def reachability_state_changed_cb(targetref, flags, info):
458+ """Callback for SCNetworkReachability API
459+
460+ This callback is passed to the SCNetworkReachability API,
461+ so its method signature has to be exactly this. Therefore,
462+ we declare it here and just call _state_changed with
463+ flags."""
464+ self._state_changed(flags)
465+
466+ c_callback = SCNRCallbackType(reachability_state_changed_cb)
467+ context = SCNRContext(0, None, None, None, None)
468+
469+ target = SCNRCreateWithName(None, HOSTNAME_TO_CHECK)
470+ if target is None:
471+ logger.error("Error creating SCNetworkReachability target")
472+ self.result_cb(UNKNOWN)
473+ return
474+
475+ ok = SCNRSetCallback(target, c_callback, pointer(context))
476+ if not ok:
477+ logger.error("error setting SCNetworkReachability callback")
478+ CFRelease(target)
479+ self.result_cb(UNKNOWN)
480+ return
481+
482+ ok = SCNRScheduleWithRunLoop(target,
483+ CFRunLoopGetCurrent(),
484+ kCFRunLoopDefaultMode)
485+ if not ok:
486+ logger.error("error scheduling on runloop: SCNetworkReachability")
487+ CFRelease(target)
488+ self.result_cb(UNKNOWN)
489+ return
490+
491+ CFRunLoopRun()
492+
493+ CFRelease(target) # won't happen
494+
495+ def _start_listening_thread(self):
496+ """Start the separate listener thread.
497+
498+ Currently will not start one more than once.
499+ Should be OK because we don't expect errors the listen method.
500+
501+ To add more error handling support, you could either add a
502+ call to join and re-start, or client could just create a new
503+ NetworkManagerState.
504+ """
505+
506+ if self.listener_thread is None:
507+ self.listener_thread = Thread(
508+ target=self._listen_on_separate_thread,
509+ name="Ubuntu SSO Network Connection Monitor")
510+ self.listener_thread.daemon = True
511+ self.listener_thread.start()
512
513 def find_online_state(self):
514- """Get the network state and return it thru the set callback."""
515+ """Calls callback with current state. Starts listening thread."""
516 try:
517- self.state_changed(NM_STATE_CONNECTED_GLOBAL)
518- except Exception, e: # pylint: disable=W0703
519- self.got_error(e)
520+ if check_connected_state():
521+ self.result_cb(ONLINE)
522+ else:
523+ self.result_cb(OFFLINE)
524+
525+ except Exception: # pylint: disable=W0703
526+ logger.exception("Getting state from SCNetworkReachability")
527+ self.result_cb(UNKNOWN)
528+ return # don't start thread on error
529+
530+ self._start_listening_thread()
531
532
533 def is_machine_connected():
534- """Return a deferred that when fired, returns if the machine is online."""
535- d = defer.Deferred()
536-
537- def got_state(state):
538- """The state was retrieved from the Network Manager."""
539- result = int(state) in NM_STATE_CONNECTED_LIST
540- d.callback(result)
541-
542- # pylint: disable=W0702, W0703
543+ """Return a deferred that when fired, returns online state as a bool.
544+
545+ Raises NetworkFailException for errors.
546+ """
547 try:
548- network = NetworkManagerState(got_state)
549- network.find_online_state()
550- except Exception, e:
551- logger.exception('is_machine_connected failed with:')
552- d.errback(NetworkFailException(e))
553- # pylint: enable=W0702, W0703
554-
555- return d
556+ return defer.succeed(check_connected_state())
557+ except Exception, e: # pylint: disable=W0703
558+ logger.exception("Exception calling check_connected_state:")
559+ return defer.fail(NetworkFailException(e))
560
561=== modified file 'ubuntu_sso/networkstate/linux.py'
562--- ubuntu_sso/networkstate/linux.py 2012-04-23 12:22:03 +0000
563+++ ubuntu_sso/networkstate/linux.py 2012-06-05 14:23:23 +0000
564@@ -117,7 +117,10 @@
565
566 def got_state(state):
567 """The state was retrieved from the Network Manager."""
568- result = int(state) in NM_STATE_CONNECTED_LIST
569+ if type(state) is not type(ONLINE):
570+ logger.exception("bad callback argument in is_machine_connected")
571+ raise NetworkFailException()
572+ result = (state == ONLINE)
573 d.callback(result)
574
575 # pylint: disable=W0702, W0703
576
577=== modified file 'ubuntu_sso/networkstate/networkstates.py'
578--- ubuntu_sso/networkstate/networkstates.py 2012-04-23 12:22:03 +0000
579+++ ubuntu_sso/networkstate/networkstates.py 2012-06-05 14:23:23 +0000
580@@ -15,14 +15,20 @@
581 # with this program. If not, see <http://www.gnu.org/licenses/>.
582 """Network states."""
583
584+
585+class NetworkState(object):
586+ """ A simple class to add a label and make debugging easier. """
587+
588+ def __init__(self, label="unlabeled"):
589+ self.label = label
590+
591+ def __repr__(self):
592+ return "Network state (%s)" % self.label
593+
594 # Values returned by the callback
595-ONLINE, OFFLINE, UNKNOWN = object(), object(), object()
596-
597-NM_STATE_NAMES = {
598- ONLINE: "online",
599- OFFLINE: "offline",
600- UNKNOWN: "unknown",
601-}
602+(ONLINE, OFFLINE, UNKNOWN) = (NetworkState("online"),
603+ NetworkState("offline"),
604+ NetworkState("unknown"))
605
606 # Internal NetworkManager State constants
607 NM_STATE_UNKNOWN = 0
608
609=== added file 'ubuntu_sso/networkstate/tests/run_nwmgr_standalone.py'
610--- ubuntu_sso/networkstate/tests/run_nwmgr_standalone.py 1970-01-01 00:00:00 +0000
611+++ ubuntu_sso/networkstate/tests/run_nwmgr_standalone.py 2012-06-05 14:23:23 +0000
612@@ -0,0 +1,39 @@
613+# -*- coding: utf-8 -*-
614+#
615+# Copyright 2012 Canonical Ltd.
616+#
617+# This program is free software: you can redistribute it and/or modify it
618+# under the terms of the GNU General Public License version 3, as published
619+# by the Free Software Foundation.
620+#
621+# This program is distributed in the hope that it will be useful, but
622+# WITHOUT ANY WARRANTY; without even the implied warranties of
623+# MERCHANTABILITY, SATISFACTORY QUALITY, or FITNESS FOR A PARTICULAR
624+# PURPOSE. See the GNU General Public License for more details.
625+#
626+# You should have received a copy of the GNU General Public License along
627+# with this program. If not, see <http://www.gnu.org/licenses/>.
628+"""
629+A test script to start a NetworkManagerState instance and print out
630+the current actual state of the system.
631+"""
632+
633+
634+from ubuntu_sso.networkstate import (NetworkManagerState,
635+ is_machine_connected)
636+
637+
638+def my_cb(state):
639+ """simple callback just prints state"""
640+ print "one.ubuntu.com:", state
641+
642+if __name__ == '__main__':
643+ # test once with direct call:
644+ is_machine_connected().addCallback(my_cb)
645+
646+ NMS = NetworkManagerState(my_cb)
647+
648+ NMS.find_online_state()
649+
650+ import time
651+ time.sleep(60)
652
653=== added file 'ubuntu_sso/networkstate/tests/test_darwin.py'
654--- ubuntu_sso/networkstate/tests/test_darwin.py 1970-01-01 00:00:00 +0000
655+++ ubuntu_sso/networkstate/tests/test_darwin.py 2012-06-05 14:23:23 +0000
656@@ -0,0 +1,193 @@
657+# -*- coding: utf-8 -*-
658+#
659+# Copyright 2010-2012 Canonical Ltd.
660+#
661+# This program is free software: you can redistribute it and/or modify it
662+# under the terms of the GNU General Public License version 3, as published
663+# by the Free Software Foundation.
664+#
665+# This program is distributed in the hope that it will be useful, but
666+# WITHOUT ANY WARRANTY; without even the implied warranties of
667+# MERCHANTABILITY, SATISFACTORY QUALITY, or FITNESS FOR A PARTICULAR
668+# PURPOSE. See the GNU General Public License for more details.
669+#
670+# You should have received a copy of the GNU General Public License along
671+# with this program. If not, see <http://www.gnu.org/licenses/>.
672+#
673+# In addition, as a special exception, the copyright holders give
674+# permission to link the code of portions of this program with the
675+# OpenSSL library under certain conditions as described in each
676+# individual source file, and distribute linked combinations
677+# including the two.
678+# You must obey the GNU General Public License in all respects
679+# for all of the code used other than OpenSSL. If you modify
680+# file(s) with this exception, you may extend this exception to your
681+# version of the file(s), but you are not obligated to do so. If you
682+# do not wish to do so, delete this exception statement from your
683+# version. If you delete this exception statement from all source
684+# files in the program, then also delete it here.
685+"""Tests for the network state detection code."""
686+
687+from twisted.internet.defer import inlineCallbacks
688+
689+from ubuntu_sso.tests import TestCase
690+from ubuntu_sso.networkstate import (
691+ darwin,
692+ NetworkFailException,
693+ )
694+
695+from ubuntu_sso.networkstate.networkstates import (
696+ ONLINE, OFFLINE, UNKNOWN,
697+ )
698+
699+from ubuntu_sso.networkstate.darwin import (
700+ is_machine_connected,
701+ NetworkManagerState
702+ )
703+
704+from ubuntu_sso.networkstate.darwin import flags_say_reachable
705+
706+
707+REACHABLE_FLAG = 1 << 1
708+CONNECTION_REQUIRED_FLAG = 1 << 2
709+
710+
711+class TestSCNRFailingInDirect(TestCase):
712+
713+ """Test that we handle a problem getting status in a direct call
714+ to check_connected_state (used by is_machine_connected) by
715+ raising an exception.
716+ """
717+
718+ def test_cant_create_target(self):
719+ """SCNRCreateWithName returning None should cause an exception."""
720+ self.patch(darwin, "SCNRCreateWithName",
721+ lambda _1, _2: None)
722+ # pylint: disable=W0212
723+ self.assertRaises(NetworkFailException,
724+ darwin.check_connected_state)
725+
726+ def test_cant_get_flags(self):
727+ """SCNRGetFlags returning False should cause an exception."""
728+ self.patch(darwin, "SCNRGetFlags",
729+ lambda _1, _2: False)
730+ # pylint: disable=W0212
731+ self.assertRaises(NetworkFailException,
732+ darwin.check_connected_state)
733+
734+
735+class TestFailingSCNRInCallbacks(TestCase):
736+
737+ """Test that we handle a problem getting status in the separate
738+ listening thread by updating the status to UNKNOWN.
739+ """
740+
741+ def expect_unknown(self, state):
742+ """A convenience callback that fails unless it sees UNKNOWN."""
743+ self.assertEquals(state, UNKNOWN)
744+
745+ def test_exc_in_find_online_state(self):
746+ """Expect UNKNOWN from find_online_state in case of exception."""
747+ def fake_check_connected_state():
748+ "fake a broken check_connected_state"
749+ raise NetworkFailException()
750+
751+ self.patch(darwin, "check_connected_state",
752+ fake_check_connected_state)
753+ NetworkManagerState(self.expect_unknown)
754+
755+ def test_cant_create_target(self):
756+ """SCNRCreateWithName returning None -> callback gets UNKNOWN."""
757+ self.patch(darwin, "SCNRCreateWithName", lambda _1, _2: None)
758+ nms = NetworkManagerState(self.expect_unknown)
759+ # pylint: disable=W0212
760+ nms._listen_on_separate_thread()
761+
762+ def test_cant_set_callback(self):
763+ """SCNRSetCallback returning false -> callback gets UNKNOWN."""
764+ self.patch(darwin, "SCNRSetCallback", lambda _1, _2, _3: False)
765+ nms = NetworkManagerState(self.expect_unknown)
766+ # pylint: disable=W0212
767+ nms._listen_on_separate_thread()
768+
769+ def test_cant_schedule_with_runloop(self):
770+ """SCNRScheduleWithRunLoop returning false -> callback gets UNKNOWN."""
771+ self.patch(darwin, "SCNRScheduleWithRunLoop",
772+ lambda _1, _2, _3: False)
773+ nms = NetworkManagerState(self.expect_unknown)
774+ # pylint: disable=W0212
775+ nms._listen_on_separate_thread()
776+
777+
778+class TestReadingFlags(TestCase):
779+ """Test interpretation of flags returned from SCNR API"""
780+
781+ def test_flag_reachable(self):
782+ """Reachable by itself is OK."""
783+ flag = REACHABLE_FLAG
784+ self.assertTrue(flags_say_reachable(flag))
785+
786+ def test_flag_reachable_and_flag_connection_required(self):
787+ """Reachable and connection-required is NOT OK"""
788+ flag = REACHABLE_FLAG | CONNECTION_REQUIRED_FLAG
789+ self.assertFalse(flags_say_reachable(flag))
790+
791+ def test_other_flagvals(self):
792+ """All other flag configurations are false for our purposes.
793+
794+ They either indicate an iOS device, which we won't run this
795+ code on, or that the server we're testing for is on this
796+ machine or wired directly to it. These cases won't happen.
797+ """
798+ for flag in range(0, 17) + [1 << 16, 1 << 17, 1 << 18]:
799+ # only test cases without the reachable bit set:
800+ flag = flag & ~ 2
801+ self.assertEqual(False, flags_say_reachable(flag))
802+
803+
804+class TestNMSListeningForNWStateChanges(TestCase):
805+ """
806+ Test that the NetworkManagerState class calls the callback with
807+ ONLINE/OFFLINE when the state changes appropriately
808+ """
809+
810+ @inlineCallbacks
811+ def setUp(self):
812+ """Setup array to hold state changes."""
813+ yield super(TestNMSListeningForNWStateChanges, self).setUp()
814+ self.network_changes = []
815+
816+ def _listen_network_changes(self, state):
817+ """Fake callback function, records state changes."""
818+ self.network_changes.append(state)
819+
820+ def test_network_state_change(self):
821+ """Test the changes in the network connection."""
822+ nms = NetworkManagerState(self._listen_network_changes)
823+ # pylint: disable=W0212
824+ nms._state_changed(2)
825+ nms._state_changed(0) # 0 or anything other than 2.
826+ nms._state_changed(2)
827+
828+ self.assertEqual(self.network_changes,
829+ [ONLINE, OFFLINE, ONLINE])
830+
831+
832+class TestIsMachineConnectedFunc(TestCase):
833+ """Simple test of is_machine_connected."""
834+
835+ @inlineCallbacks
836+ def test_not_connected_returns_false(self):
837+ """test that False comes back False"""
838+ self.patch(darwin, "check_connected_state",
839+ lambda: False)
840+ con = yield is_machine_connected()
841+ self.assertEqual(con, False)
842+
843+ @inlineCallbacks
844+ def test_connected_returns_true(self):
845+ """check that True comes back True"""
846+ self.patch(darwin, "check_connected_state",
847+ lambda: True)
848+ con = yield is_machine_connected()
849+ self.assertEqual(con, True)
850
851=== modified file 'ubuntu_sso/networkstate/tests/test_linux.py'
852--- ubuntu_sso/networkstate/tests/test_linux.py 2012-04-19 19:29:37 +0000
853+++ ubuntu_sso/networkstate/tests/test_linux.py 2012-06-05 14:23:23 +0000
854@@ -55,7 +55,7 @@
855 NM_STATE_UNKNOWN,
856 )
857
858-from ubuntu_sso.networkstate.linux import (is_machine_connected,
859+from ubuntu_sso.networkstate.linux import (is_machine_connected,
860 DBUS_UNKNOWN_SERVICE,
861 NM_DBUS_INTERFACE,
862 NM_DBUS_OBJECTPATH,
863@@ -178,98 +178,38 @@
864 self.assertFalse(nms.state_signal.removed)
865
866 @inlineCallbacks
867- def test_is_machine_connected_nm_state_connected_old(self):
868- """Fake the NetworkManagerState with connected return."""
869- self.patch(FakeNetworkManagerState, "connection_state",
870- NM_STATE_CONNECTED_OLD)
871- d = yield is_machine_connected()
872- self.assertTrue(d)
873-
874- @inlineCallbacks
875- def test_is_machine_connected_nm_state_connected_global(self):
876- """Fake the NetworkManagerState with connected return."""
877- self.patch(FakeNetworkManagerState, "connection_state",
878- NM_STATE_CONNECTED_GLOBAL)
879- d = yield is_machine_connected()
880- self.assertTrue(d)
881-
882- @inlineCallbacks
883- def test_is_machine_disconnected_nm_state_nm_state_unknown(self):
884- """Fake the NetworkManagerState with disconnected return."""
885- self.patch(FakeNetworkManagerState, "connection_state",
886- NM_STATE_UNKNOWN)
887- d = yield is_machine_connected()
888- self.assertFalse(d)
889-
890- @inlineCallbacks
891- def test_is_machine_disconnected_nm_state_asleep_old(self):
892- """Fake the NetworkManagerState with disconnected return."""
893- self.patch(FakeNetworkManagerState, "connection_state",
894- NM_STATE_ASLEEP_OLD)
895- d = yield is_machine_connected()
896- self.assertFalse(d)
897-
898- @inlineCallbacks
899- def test_is_machine_disconnected_nm_state_asleep(self):
900- """Fake the NetworkManagerState with disconnected return."""
901- self.patch(FakeNetworkManagerState, "connection_state",
902- NM_STATE_ASLEEP)
903- d = yield is_machine_connected()
904- self.assertFalse(d)
905-
906- @inlineCallbacks
907- def test_is_machine_disconnected_nm_state_connecting_old(self):
908- """Fake the NetworkManagerState with disconnected return."""
909- self.patch(FakeNetworkManagerState, "connection_state",
910- NM_STATE_CONNECTING_OLD)
911- d = yield is_machine_connected()
912- self.assertFalse(d)
913-
914- @inlineCallbacks
915- def test_is_machine_disconnected_nm_state_connecting(self):
916- """Fake the NetworkManagerState with disconnected return."""
917- self.patch(FakeNetworkManagerState, "connection_state",
918- NM_STATE_CONNECTING)
919- d = yield is_machine_connected()
920- self.assertFalse(d)
921-
922- @inlineCallbacks
923- def test_is_machine_disconnected_nm_state_connected_local(self):
924- """Fake the NetworkManagerState with disconnected return."""
925- self.patch(FakeNetworkManagerState, "connection_state",
926- NM_STATE_CONNECTED_LOCAL)
927- d = yield is_machine_connected()
928- self.assertFalse(d)
929-
930- @inlineCallbacks
931- def test_is_machine_disconnected_nm_state_connected_site(self):
932- """Fake the NetworkManagerState with disconnected return."""
933- self.patch(FakeNetworkManagerState, "connection_state",
934- NM_STATE_CONNECTED_SITE)
935- d = yield is_machine_connected()
936- self.assertFalse(d)
937-
938- @inlineCallbacks
939- def test_is_machine_disconnected_nm_state_disconnected_old(self):
940- """Fake the NetworkManagerState with disconnected return."""
941- self.patch(FakeNetworkManagerState, "connection_state",
942- NM_STATE_DISCONNECTED_OLD)
943- d = yield is_machine_connected()
944- self.assertFalse(d)
945-
946- @inlineCallbacks
947- def test_is_machine_disconnected_nm_state_disconnected(self):
948- """Fake the NetworkManagerState with disconnected return."""
949- self.patch(FakeNetworkManagerState, "connection_state",
950- NM_STATE_DISCONNECTED)
951- d = yield is_machine_connected()
952- self.assertFalse(d)
953-
954- @inlineCallbacks
955- def test_is_machine_defer_error(self):
956- """Fake the NetworkManagerState with disconnected return."""
957- self.patch(FakeNetworkManagerState, "connection_state",
958- 'no-int')
959+ def test_is_machine_connected_nm_state_online(self):
960+ """Callback given ONLINE should mean we are online"""
961+ self.patch(FakeNetworkManagerState, "connection_state",
962+ ONLINE)
963+ d = yield is_machine_connected()
964+ self.assertTrue(d)
965+
966+ @inlineCallbacks
967+ def test_is_machine_connected_nm_state_offline(self):
968+ """Callback given OFFLINE should mean we are offline"""
969+ self.patch(FakeNetworkManagerState, "connection_state",
970+ OFFLINE)
971+ d = yield is_machine_connected()
972+ self.assertFalse(d)
973+
974+ @inlineCallbacks
975+ def test_is_machine_connected_nm_state_unknown(self):
976+ """Callback given ONLINE should mean we are not online"""
977+ self.patch(FakeNetworkManagerState, "connection_state",
978+ UNKNOWN)
979+ d = yield is_machine_connected()
980+ self.assertFalse(d)
981+
982+ @inlineCallbacks
983+ def test_is_machine_connected_callback_error(self):
984+ """Test bad argument to is_machine_connected's internal callback.
985+
986+ Passing anything other than ONLINE/OFFLINE/UNKNOWN should
987+ cause an exception.
988+ """
989+ self.patch(FakeNetworkManagerState, "connection_state",
990+ NM_STATE_CONNECTED_GLOBAL)
991 yield self.assertFailure(is_machine_connected(), NetworkFailException)
992
993
994@@ -361,6 +301,18 @@
995 super(NetworkManagerStateTestCase, self).setUp()
996 self.connect_proxy()
997
998+ def test_nm_asleep(self):
999+ """Asleep status should mean offline."""
1000+ self.check_nm_state(self.assertOffline, NM_STATE_ASLEEP)
1001+
1002+ def test_nm_asleep_old(self):
1003+ """Asleep, old status, should mean offline."""
1004+ self.check_nm_state(self.assertOffline, NM_STATE_ASLEEP_OLD)
1005+
1006+ def test_nm_unknown(self):
1007+ """Unknown status should be treated the same as OFFLINE."""
1008+ self.check_nm_state(self.assertOffline, NM_STATE_UNKNOWN)
1009+
1010 def test_nm_online_old(self):
1011 """Check the connected, old status, case."""
1012 self.check_nm_state(self.assertOnline, NM_STATE_CONNECTED_OLD)
1013
1014=== modified file 'ubuntu_sso/tests/test_account.py'
1015--- ubuntu_sso/tests/test_account.py 2012-04-09 17:38:24 +0000
1016+++ ubuntu_sso/tests/test_account.py 2012-06-05 14:23:23 +0000
1017@@ -34,7 +34,6 @@
1018 import os
1019 import urllib2
1020
1021-from oauth import oauth
1022 from twisted.trial.unittest import TestCase
1023 from twisted.internet import defer
1024
1025@@ -51,7 +50,6 @@
1026 ResetPasswordTokenError,
1027 SSO_STATUS_OK,
1028 SSO_STATUS_ERROR,
1029- TimestampedAuthorizer,
1030 )
1031 from ubuntu_sso.tests import (
1032 APP_NAME,
1033@@ -203,36 +201,6 @@
1034 return defer.fail(e)
1035
1036
1037-class TimestampedAuthorizerTestCase(TestCase):
1038- """Test suite for the TimestampedAuthorizer."""
1039-
1040- def test_authorize_request_includes_timestamp(self):
1041- """The authorizeRequest method includes the timestamp."""
1042- fromcandt_call = []
1043- fake_uri = "http://protocultura.net"
1044- fake_timestamp = 1234
1045- get_fake_timestamp = lambda: fake_timestamp
1046- original_oauthrequest = oauth.OAuthRequest
1047-
1048- class FakeOAuthRequest(oauth.OAuthRequest):
1049- """A Fake OAuthRequest class."""
1050-
1051- @staticmethod
1052- def from_consumer_and_token(*args, **kwargs):
1053- """A fake from_consumer_and_token."""
1054- fromcandt_call.append((args, kwargs))
1055- builder = original_oauthrequest.from_consumer_and_token
1056- return builder(*args, **kwargs)
1057-
1058- self.patch(oauth, "OAuthRequest", FakeOAuthRequest)
1059-
1060- authorizer = TimestampedAuthorizer(get_fake_timestamp, "ubuntuone")
1061- authorizer.authorizeRequest(fake_uri, "POST", None, {})
1062- call_kwargs = fromcandt_call[0][1]
1063- parameters = call_kwargs["parameters"]
1064- self.assertEqual(parameters["oauth_timestamp"], fake_timestamp)
1065-
1066-
1067 class AccountTestCase(TestCase):
1068 """Test suite for the SSO login processor."""
1069

Subscribers

People subscribed via source and target branches

to all changes: