From e2a470e6b2e33906fc149df0bed234b2aef2a050 Mon Sep 17 00:00:00 2001 From: Fredrik Wendt Date: Mon, 23 Aug 2010 22:44:08 +0100 Subject: [PATCH] rewrote settings and daemon - Bug #11109 --- package/src/wifi_assistant/daemon.py | 108 +++++++++------- package/src/wifi_assistant/launcher.py | 10 ++ .../wifi_assistant/settings/ApplicationSettings.py | 43 +++++++ .../src/wifi_assistant/settings/NetworkSetting.py | 49 ++++++++ .../src/wifi_assistant/settings/NetworkSettings.py | 133 ++++++++++++++++++++ .../test/unit/settings/test_ApplicationSettings.py | 75 +++++++++++ package/test/unit/settings/test_NetworkSettings.py | 110 ++++++++++++++++ package/test/unit/test_Daemon.py | 97 ++++++++++++++ 8 files changed, 578 insertions(+), 47 deletions(-) create mode 100644 package/src/wifi_assistant/settings/ApplicationSettings.py create mode 100644 package/src/wifi_assistant/settings/NetworkSetting.py create mode 100644 package/src/wifi_assistant/settings/NetworkSettings.py create mode 100644 package/test/unit/settings/__init__.py create mode 100644 package/test/unit/settings/test_ApplicationSettings.py create mode 100644 package/test/unit/settings/test_NetworkSettings.py create mode 100644 package/test/unit/test_Daemon.py diff --git a/package/src/wifi_assistant/daemon.py b/package/src/wifi_assistant/daemon.py index ab60b34..d8d3215 100644 --- a/package/src/wifi_assistant/daemon.py +++ b/package/src/wifi_assistant/daemon.py @@ -4,29 +4,75 @@ import conic import gobject from launcher import Launcher -from settings import Settings def _(str): return str class Daemon(): - def __init__(self, launcher, settings, parent_window): - self._settings = settings + def __init__(self, launcher, application_settings, network_settings, parent_window): + self._application_settings = application_settings + self._network_settings = network_settings self._parent = parent_window self._launcher = launcher - self._popup = self._settings.getUsePopup() + self._popup = self._application_settings.getUsePopup() - def launch_browser(self): - url = self._settings.getUrlToOpen() - self._launcher.openUrl(url) + def connectionEstablished(self, ssid): + settings = self._network_settings.get(ssid) + if settings is None: + if self.showDecisionDialog(ssid): + defaults = self._network_settings.getDefaultSettings() + self.launchBrowser(defaults) + return + + if settings.getLaunchingOfBrowserEnabled(): + self.launchBrowser(settings) + + def launchBrowser(self, settings): + browser_name = settings.getNameOfBrowserToLaunch() + browser_options = settings.getBrowserOptions() + if 'url' in browser_options: + self._launcher.launchBrowser(browser_name, browser_options) + # the following line is the backwards compatible line + else: + settings = self._network_settings.getDefaultSettings() + self.launchBrowser(settings) - def show_decision_dialog(self, ssid): + def showDecisionDialog(self, ssid): if not self._popup: - return + return False + + dialog = self._createDialog(ssid) + + dialog.show_all() + result = dialog.run() + dialog.hide() + + launch_browser = (result == gtk.RESPONSE_YES) + if checkbox.get_active(): + setting = NetworkSetting() + setting.setNetworkName(ssid) + setting.setLaunchingOfBrowserEnabled(launch_browser) + self._network_settings.save(setting) + return launch_browser + + def start(self): + self._connection = conic.Connection() + self._connection.connect("connection-event", self._connectionEventCallback) + self._connection.set_property("automatic-connection-events", True) + self._application_settings.addUsePopupListener(self._usePopupEventCallback) + gtk.main() + + + def stop(self): + self._application_settings.removeUsePopupListener(self._usePopupEventCallback) + self._connection.set_property("automatic-connection-events", False) + + + def _createDialog(self, ssid): dialog = gtk.Dialog(ssid, self._parent) dialog.vbox.set_homogeneous(False) @@ -44,29 +90,10 @@ class Daemon(): checkbox.set_active(True) dialog.vbox.add(checkbox) - dialog.show_all() - result = dialog.run() - dialog.hide() - - launchBrowser = (result == gtk.RESPONSE_YES) - if checkbox.get_active(): - self._settings.setLaunchSetting(ssid, launchBrowser) - - return launchBrowser + return dialog - - def connection_established(self, ssid): - value = self._settings.getLaunchSetting(ssid) - if value is None: - if self.show_decision_dialog(ssid): - self.launch_browser() - return - - if value: - self.launch_browser() - - - def _connection_cb(self, connection, event): + + def _connectionEventCallback(self, connection, event): status = event.get_status() if status == conic.STATUS_CONNECTED: # assemble id > name dict @@ -82,28 +109,15 @@ class Daemon(): iap_name = None if (iap_id_to_name.has_key(iap_id)): iap_name = iap_id_to_name[iap_id] - self.connection_established(iap_name) + self.connectionEstablished(iap_name) - def start(self): - self._connection = conic.Connection() - self._connection.connect("connection-event", self._connection_cb) - self._connection.set_property("automatic-connection-events", True) - self._settings.addUsePopupListener(self._activateCallback) - gtk.main() - - - def stop(self): - self._settings.removeUsePopupListener(self._activateCallback) - self._connection.set_property("automatic-connection-events", False) - - - def _activateCallback(self, gconfClient, id, gconfEntry, x): + def _usePopupEventCallback(self, gconfClient, id, gconfEntry, x): self._popup = gconfEntry.get_value().get_bool() if __name__ == "__main__": - d = Daemon(Launcher(), Settings(), gtk.Window()) + d = Daemon(Launcher(), ApplicationSettings(), NetworkSettings(), gtk.Window()) d.start() d.stop() diff --git a/package/src/wifi_assistant/launcher.py b/package/src/wifi_assistant/launcher.py index f8dbb5d..721c33a 100644 --- a/package/src/wifi_assistant/launcher.py +++ b/package/src/wifi_assistant/launcher.py @@ -10,8 +10,18 @@ class Launcher(): pass # ---- public API __________________________________________________________ + + def launchBrowser(self, browser_name, browser_options): + """Uses the specified browser and makes the calls specified in the browser_options""" + # TODO: + + url = browser_options['url'] + self.openUrl(url) + def openUrl(self, url): + """Uses the default browser to open the specified url.""" + osso_context = osso.Context("org.maemo.touchsearch", "1.1", False) osso_rpc = osso.Rpc(osso_context) osso_rpc.rpc_run_with_defaults("osso_browser", "open_new_window", (url,)) diff --git a/package/src/wifi_assistant/settings/ApplicationSettings.py b/package/src/wifi_assistant/settings/ApplicationSettings.py new file mode 100644 index 0000000..ff161a2 --- /dev/null +++ b/package/src/wifi_assistant/settings/ApplicationSettings.py @@ -0,0 +1,43 @@ +#!/usr/bin/python2.5 +from gnome import gconf + +class ApplicationSettings(): + + def __init__(self, gconf_client, gconf_root_dir='/apps/maemo/wifi-assistant'): + self._gc = gconf_client + self._gconfRootDir = gconf_root_dir + self._gconfPopupKey = gconf_root_dir + '/daemon' + self._listeners = {} + + # ---- public API __________________________________________________________ + + def registerUsePopupListener(self, callback): + """Registers a listener/callback to changes on Use Daemon setting""" + + if len(self._listeners) == 0: + self._gc.add_dir(self._gconfRootDir, gconf.CLIENT_PRELOAD_NONE) + ref_id = self._gc.notify_add(self._gconfPopupKey, callback) + self._listeners[callback] = ref_id + + + def unregisterUsePopupListener(self, callback): + """Unregisters the listener/callback""" + + if (self._listeners.has_key(callback)): + ref_id = self._listeners.pop(callback) + self._gc.notify_remove(ref_id) + + if len(self._listeners) == 0: + self._gc.remove_dir(self._gconfRootDir) + + + def getUsePopup(self): + """Tells whether to use the daemon or not.""" + + return self._gc.get_bool(self._gconfPopupKey) is True + + + def setUsePopup(self, mode): + """mode is either True or False.""" + + self._gc.set_bool(self._gconfPopupKey, mode) diff --git a/package/src/wifi_assistant/settings/NetworkSetting.py b/package/src/wifi_assistant/settings/NetworkSetting.py new file mode 100644 index 0000000..065face --- /dev/null +++ b/package/src/wifi_assistant/settings/NetworkSetting.py @@ -0,0 +1,49 @@ + +class NetworkSetting(object): + ''' + The settings specified for a network + ''' + + def __init__(self, settings=None): + self._attributes = {} + if (settings): + for key in settings: + self._attributes[key] = settings[key] + if not self._attributes.has_key('launch'): + self.setLaunchingOfBrowserEnabled(False) + + def getNetworkName(self): + return self._attributes['name'] + + + def setNetworkName(self, name): + self._attributes['name'] = name + + + def getLaunchingOfBrowserEnabled(self): + return self._attributes['launch'] is True + + + def setLaunchingOfBrowserEnabled(self, value): + self._attributes['launch'] = value + + + def getNameOfBrowserToLaunch(self): + if self._attributes.has_key('browser'): + return self._attributes['browser'] + return "default" + + + def setNameOfBrowserToLaunch(self, browser): + self._attributes['browser'] = browser + + + def getBrowserOptions(self): + if self._attributes.has_key('options'): + return self._attributes['options'] + return {} + + + def setBrowserOptions(self, options): + self._attributes['options'] = options + \ No newline at end of file diff --git a/package/src/wifi_assistant/settings/NetworkSettings.py b/package/src/wifi_assistant/settings/NetworkSettings.py new file mode 100644 index 0000000..0eacaeb --- /dev/null +++ b/package/src/wifi_assistant/settings/NetworkSettings.py @@ -0,0 +1,133 @@ +import gnome.gconf as gconf +from NetworkSetting import NetworkSetting +import gtk, gobject + + +class NetworkSettings(object): + ''' + Reads and Writes NetworkSettings from/to GConf. + ''' + + __DEFAULT_SETTINGS = 'default_settings' + + def __init__(self, gconf_client, root_path): + self._gc = gconf_client + self._root_path = root_path + + + def delete(self, launch_setting): + key = self._assembleRootKeyForSsid(launch_setting.getNetworkName()) + self._recursive_unset(key) + + + def getDefaultSettings(self): + settings = self.get(self.__DEFAULT_SETTINGS) + if settings is None: + settings = NetworkSetting() + settings.setNetworkName(self.__DEFAULT_SETTINGS) + settings.setLaunchingOfBrowserEnabled(True) + settings.setBrowserOptions({'url':'http://wifi-assistant.garage.maemo.org/'}) + self.save(settings) + return settings + + + def getListStore(self): + """Returns a ListStore with one Row per Network Setting""" + + store = gtk.ListStore(str, gobject.TYPE_BOOLEAN) + settings = self._getAllNetworkSettings() + for setting in settings: + store.append([setting.getNetworkName(), setting.getLaunchingOfBrowserEnabled()]) + return store + + + def get(self, ssid): + key = self._assembleRootKeyForSsid(ssid) + if self._gc.dir_exists(key): + return NetworkSetting(self._loadValuesFromGConf(key)) + print "WARNING: No settings to return for network name", ssid + return None + + + def save(self, launch_setting): + self._saveValuesToGConf(launch_setting) + + + def _assembleRootKeyForSsid(self, ssid): + safe = self._replaceForbiddenCharacters(ssid) + return self._root_path + '/' + safe + + + def _getAllNetworkSettings(self): + dirs = self._gc.all_dirs(self._root_path) + settings = [] + key_offset = len(self._root_path) + 1 + for dir in dirs: + key = dir[key_offset:] + if self.__DEFAULT_SETTINGS != key: + settings.append(self.get(key)) + return settings + + + def _loadValuesFromGConf(self, path): + """Loads all values under a given path in gconf recursively into a dict""" + + values = {} + path_length = len(path) + 1 # remove trailing / too + + dirs = self._gc.all_dirs(path) + for sub_dir_path in dirs: + key = sub_dir_path[path_length:] + values[key] = self._loadValuesFromGConf(sub_dir_path) + + entries = self._gc.all_entries(path) + for entry in entries: + full_key = entry.get_key() + key = full_key[path_length:] + gvalue = entry.get_value() + if gvalue.type == gconf.VALUE_BOOL: + values[key] = gvalue.get_bool() + elif gvalue.type == gconf.VALUE_STRING: + values[key] = gvalue.get_string() + else: + print 'ga' + + return values + + + def _recursive_unset(self, dir): + # there's no recursive_unset available in gconf so we'll have to do it ourselves + all_entries = self._gc.all_entries(dir) + for entry in all_entries: + self._gc.unset(entry.get_key()) + for sub_dir in self._gc.all_dirs(dir): + self._recursive_unset(sub_dir) + self._gc.suggest_sync() + + + def _replaceForbiddenCharacters(self, str): + allowed = 'abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789-_' + result = '' + for c in str: + if c in allowed: + result = result + c + else: + result = result + '__' + return result + + + def _saveValuesToGConf(self, launch_setting): + """Saves all values "recursively" from NetworkSetting""" + + network_name = launch_setting.getNetworkName() + key = self._assembleRootKeyForSsid(network_name) + browser = launch_setting.getNameOfBrowserToLaunch() + launch = launch_setting.getLaunchingOfBrowserEnabled() + options = launch_setting.getBrowserOptions() + + self._gc.set_string(key + "/name", network_name) + self._gc.set_string(key + '/browser', browser) + self._gc.set_bool(key + '/launch', launch) + for option_key in options: + self._gc.set_string(key + "/options/" + option_key, options[option_key]) + \ No newline at end of file diff --git a/package/test/unit/settings/__init__.py b/package/test/unit/settings/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/package/test/unit/settings/test_ApplicationSettings.py b/package/test/unit/settings/test_ApplicationSettings.py new file mode 100644 index 0000000..354c3f0 --- /dev/null +++ b/package/test/unit/settings/test_ApplicationSettings.py @@ -0,0 +1,75 @@ +from gnome import gconf +from wifi_assistant.settings.ApplicationSettings import ApplicationSettings + +import unittest + +class ApplicationSettingsTest(unittest.TestCase): + + def setUp(self): + self.gc = gconf.client_get_default() + self.root = '/apps/maemo/wifi/unit_tests' + self._clearGConf(self.root) + self._setupDefaultValues(self.root) + self._firstCallbackCalled = None + self._secondCallbackCalled = None + self.testee = ApplicationSettings(self.gc, self.root) + + def _setupDefaultValues(self, dir): + self.gc.set_bool(dir + '/daemon', True) + + # FIXME: inherit + def _clearGConf(self, dir): + # there's no recursive_unset available + all_entries = self.gc.all_entries(dir) + for entry in all_entries: + self.gc.unset(entry.get_key()) + for sub_dir in self.gc.all_dirs(dir): + self._clearGConf(sub_dir) + self.gc.suggest_sync() + + + def test_default_is_to_use_popup(self): + assert self.testee.getUsePopup() is True + + + def test_setting_use_popup_works(self): + assert self.testee.getUsePopup() is True + + self.testee.setUsePopup(False) + assert self.testee.getUsePopup() is False + + self.testee.setUsePopup(True) + assert self.testee.getUsePopup() is True + + + def dont_test_adding_two_listeners_removing_one_still_notifies_first_listener(self): + firstSignal = False + secondSignal = True + + self.testee.registerUsePopupListener(self._firstCallback) + self.testee.registerUsePopupListener(self._secondCallback) + + # TODO: start thread to have signalling work properly ... + + self.testee.setUsePopup(firstSignal) + + assert self._firstCallbackCalled == firstSignal + assert self._secondCallbackCalled == firstSignal + + self.testee.unregisterUsePopupListener(self._secondCallback) + self.testee.setUsePopup(secondSignal) + + assert self._firstCallbackCalled == secondSignal + assert self._secondCallbackCalled == firstSignal + + + def _firstCallback(self, value): + self._firstCallbackCalled = value + + def _secondCallback(self, value): + self._secondCallbackCalled = value + + +if __name__ == '__main__': + unittest.main() + diff --git a/package/test/unit/settings/test_NetworkSettings.py b/package/test/unit/settings/test_NetworkSettings.py new file mode 100644 index 0000000..1db0aa6 --- /dev/null +++ b/package/test/unit/settings/test_NetworkSettings.py @@ -0,0 +1,110 @@ +from gnome import gconf +from wifi_assistant.settings.NetworkSettings import NetworkSettings +from wifi_assistant.settings.NetworkSetting import NetworkSetting + +import unittest + +class NetworkSettingsTest(unittest.TestCase): + + def setUp(self): + self.gc = gconf.client_get_default() + self.root = '/apps/maemo/wifi/unit_tests' + self._clearGConf(self.root) + self._setupDefaultValues(self.root) + self.testee = NetworkSettings(self.gc, self.root) + + def _setupDefaultValues(self, dir): + #settings = NetworkSetting() + #settings.setBrowserOption("url", "http://wifi-assistant.wendt.se/") + #settings.setNameOfBrowserToLaunch("default") + #settings.setNetworkName("default_settings") + #self.testee.save(settings) + #self.gc.set_string(dir + '/default_settings/options/url', 'http://wifi-assistant.wendt.se/') + #self.gc.set_string(dir + '/default_settings/browser', 'default') + # moved this into code for now + return + + def _clearGConf(self, dir): + # there's no recursive_unset available + all_entries = self.gc.all_entries(dir) + for entry in all_entries: + self.gc.unset(entry.get_key()) + for sub_dir in self.gc.all_dirs(dir): + self._clearGConf(sub_dir) + self.gc.suggest_sync() + + + def _createSettings(self, ssid): + return NetworkSetting({'name':ssid}) + + + def test_an_unkown_ssid_returns_None(self): + settings = self.testee.get('unknown') + assert settings is None + + + def test_saving_works(self): + ssid = 'test' + settings = self._createSettings(ssid) + + # make sure it's empty first + assert self.testee.get(ssid) is None + + self.testee.save(settings) + assert self.testee.get(ssid) is not None + + + def test_removing_network_setting_works(self): + ssid = 'test' + settings = self._createSettings(ssid) + + # make sure it's empty first + assert self.testee.get(ssid) is None + + self.testee.save(settings) + assert self.testee.get(ssid) is not None + + self.testee.delete(settings) + value = self.testee.get(ssid) + assert value is None + + + def test_network_name_can_contain_space_dash_and_underscore(self): + ssid = 'This Is-My_Network' + settings = self._createSettings(ssid) + + # make sure it's empty first + assert self.testee.get(ssid) is None + + self.testee.save(settings) + value = self.testee.get(ssid) + assert value is not None + assert value.getNetworkName() == ssid + + + def test_default_settings(self): + defaults = self.testee.getDefaultSettings() + assert defaults.getNameOfBrowserToLaunch() == 'default' + browser_options = defaults.getBrowserOptions() + assert defaults.getLaunchingOfBrowserEnabled() is True + assert browser_options is not None + assert browser_options.has_key('url') + assert "http://" in browser_options['url'] + + + def test_list(self): + ssid = 'This Is-My_Network' + settings = self._createSettings(ssid) + self.testee.save(settings) + + store = self.testee.getListStore() + assert store is not None + store_len = len(store) + assert store_len == 1 + + # FIXME: assert that default_settings are not there + + +if __name__ == '__main__': + unittest.main() + diff --git a/package/test/unit/test_Daemon.py b/package/test/unit/test_Daemon.py new file mode 100644 index 0000000..9e7555b --- /dev/null +++ b/package/test/unit/test_Daemon.py @@ -0,0 +1,97 @@ +from gnome import gconf +from wifi_assistant.daemon import Daemon +from wifi_assistant.settings.NetworkSetting import NetworkSetting + +import unittest +from pie import * + +url_to_open = 'http://wifi-assistant.garage.maemo.org' +default_browser_name = "default" +default_browser_options = {'url':url_to_open} +default_settings = NetworkSetting() +default_settings.setNameOfBrowserToLaunch(default_browser_name) +default_settings.setBrowserOptions(default_browser_options) +ssid = 'A Network Name' +settings_without_options = NetworkSetting() +settings_without_options.setNetworkName(ssid) +settings_without_options.setLaunchingOfBrowserEnabled(True) + +class DaemonTest(unittest.TestCase): + + def replayMocks(self): + self.application_settings.replay() + self.launcher.replay() + self.network_settings.replay() + + def setUp(self): + self.launcher = Mock() + self.application_settings = Mock() + self.network_settings = Mock() + self.parent_window = None + + def createTesteeWithPopupClickingYes(self): + given(self.application_settings).getUsePopup().willReturn(True) + self.testee = Daemon(self.launcher, self.application_settings, self.network_settings, self.parent_window) + + def yes(*args, **kwargs): + return True + self.testee.showDecisionDialog = yes + + def createTesteeWithPopupClickingNo(self): + given(self.application_settings).getUsePopup().willReturn(True) + self.testee = Daemon(self.launcher, self.application_settings, self.network_settings, self.parent_window) + + def no(*args, **kwargs): + return False + self.testee.showDecisionDialog = no + + def test_withANewNetwork_whenUserClicksYes_browserIsLaunched(self): + # GIVEN + self.createTesteeWithPopupClickingYes() + given(self.network_settings).get(ssid).willReturn(None) + given(self.network_settings).getDefaultSettings().willReturn(default_settings) + + # WHEN + self.replayMocks() + self.testee.connectionEstablished(ssid) + + # THEN + verify(self.network_settings).getDefaultSettings() + verify(self.launcher).launchBrowser(default_browser_name, default_browser_options) + + def test_withANewNetwork_whenUserClicksNo_browserIsNotLaunched(self): + self.createTesteeWithPopupClickingNo() + given(self.network_settings).get(ssid).willReturn(None) + + # WHEN + self.replayMocks() + self.testee.connectionEstablished(ssid) + + # THEN + verify(self.launcher, never()).launchBrowser(default_browser_name, default_browser_options) + + def test_popupIsNotOpenedIfPopupIsDisabledInApplicationSettings(self): + given(self.application_settings).getUsePopup().willReturn(False) + self.testee = Daemon(self.launcher, self.application_settings, self.network_settings, self.parent_window) + + self.replayMocks() + self.testee.connectionEstablished(ssid) + + verify(self.launcher, never()).launchBrowser() + verify(self.network_settings, never()).get(ssid) + + def test_knownSsidWithLaunchSetToTrueUsesDefaultBrowserAndUrl(self): + given(self.network_settings).get(ssid).willReturn(settings_without_options) + given(self.network_settings).getDefaultSettings().willReturn(default_settings) + given(self.application_settings).getUsePopup().willReturn(True) + self.testee = Daemon(self.launcher, self.application_settings, self.network_settings, self.parent_window) + + self.replayMocks() + self.testee.connectionEstablished(ssid) + + verify(self.launcher, never()).launchBrowser(default_browser_name, default_browser_options) + + +if __name__ == '__main__': + unittest.main() + -- 1.7.9.5