unity-scope-click-0.1+14.04.20140417/0000755000015301777760000000000012323764741017325 5ustar pbusernogroup00000000000000unity-scope-click-0.1+14.04.20140417/autopilot/0000755000015301777760000000000012323764741021345 5ustar pbusernogroup00000000000000unity-scope-click-0.1+14.04.20140417/autopilot/unityclickscope/0000755000015301777760000000000012323764741024555 5ustar pbusernogroup00000000000000unity-scope-click-0.1+14.04.20140417/autopilot/unityclickscope/fake_services.py0000644000015301777760000000431012323764232027731 0ustar pbusernogroup00000000000000# -*- Mode: Python; coding: utf-8; indent-tabs-mode: nil; tab-width: 4 -*- # # Copyright (C) 2014 Canonical Ltd. # # This program is free software; you can redistribute it and/or modify # it under the terms of the GNU General Public License version 3, as published # by the Free Software Foundation. # # This program is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # GNU General Public License for more details. # # You should have received a copy of the GNU General Public License # along with this program. If not, see . import dbus import dbusmock class FakeDownloadService(object): """Fake Download Service using a dbusmock interface.""" def __init__(self, dbus_connection): super(FakeDownloadService, self).__init__() self.dbus_connection = dbus_connection self.mock = self._get_mock_interface() def _get_mock_interface(self): return dbus.Interface( self.dbus_connection.get_object( 'com.canonical.applications.Downloader', '/'), dbusmock.MOCK_IFACE) def add_method(self, method, return_value): if method == 'getAllDownloadsWithMetadata': self._add_getAllDownloadsWithMetadata(return_value) elif method == 'createDownload': self._add_createDownload(return_value) else: raise NotImplementedError('Unknown method: {}'.format(method)) def _add_getAllDownloadsWithMetadata(self, return_value): self.mock.AddMethod( 'com.canonical.applications.DownloadManager', 'getAllDownloadsWithMetadata', 'ss', 'ao', 'ret = {}'.format(return_value)) def _add_createDownload(self, return_value): self.mock.AddMethod( 'com.canonical.applications.DownloadManager', 'createDownload', '(sssa{sv}a{ss})', 'o', 'ret = "{}"'.format(return_value)) def add_download_object(self, object_path): self.mock.AddObject( object_path, 'com.canonical.applications.Download', {}, [('start', '', '', '')]) unity-scope-click-0.1+14.04.20140417/autopilot/unityclickscope/fixture_setup.py0000644000015301777760000000427612323764232030041 0ustar pbusernogroup00000000000000# -*- Mode: Python; coding: utf-8; indent-tabs-mode: nil; tab-width: 4 -*- # # Copyright (C) 2013, 2014 Canonical Ltd. # # This program is free software; you can redistribute it and/or modify # it under the terms of the GNU General Public License version 3, as published # by the Free Software Foundation. # # This program is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # GNU General Public License for more details. # # You should have received a copy of the GNU General Public License # along with this program. If not, see . """Set up and clean up fixtures for the Unity Click Scope acceptance tests.""" import logging import threading import fixtures from unityclickscope import fake_servers logger = logging.getLogger(__name__) class FakeServerRunning(fixtures.Fixture): def __init__(self, server_class): super(FakeServerRunning, self).__init__() self.server_class = server_class def setUp(self): super(FakeServerRunning, self).setUp() self._start_fake_server() def _start_fake_server(self): logger.info('Starting fake server: {}.'.format(self.server_class)) server_address = ('', 0) fake_server = self.server_class(server_address) server_thread = threading.Thread(target=fake_server.serve_forever) server_thread.start() logger.info('Serving at port {}.'.format(fake_server.server_port)) self.addCleanup(self._stop_fake_server, server_thread, fake_server) self.url = 'http://localhost:{}/'.format(fake_server.server_port) def _stop_fake_server(self, thread, server): logger.info('Stopping fake server: {}.'.format(self.server_class)) server.shutdown() thread.join() class FakeSearchServerRunning(FakeServerRunning): def __init__(self): super(FakeSearchServerRunning, self).__init__( fake_servers.FakeSearchServer) class FakeDownloadServerRunning(FakeServerRunning): def __init__(self): super(FakeDownloadServerRunning, self).__init__( fake_servers.FakeDownloadServer) unity-scope-click-0.1+14.04.20140417/autopilot/unityclickscope/credentials.py0000644000015301777760000000710712323764232027424 0ustar pbusernogroup00000000000000# -*- Mode: Python; coding: utf-8; indent-tabs-mode: nil; tab-width: 4 -*- # # Copyright (C) 2014 Canonical Ltd. # # This program is free software; you can redistribute it and/or modify # it under the terms of the GNU General Public License version 3, as published # by the Free Software Foundation. # # This program is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # GNU General Public License for more details. # # You should have received a copy of the GNU General Public License # along with this program. If not, see . import threading from gi.repository import Accounts, GLib, Signon class CredentialsException(Exception): """Exception for credentials problems.""" class AccountManager(object): def __init__(self): self._manager = Accounts.Manager() def _start_main_loop(self): self.error = None self._main_loop = GLib.MainLoop() self._main_loop_thread = threading.Thread( target=self._main_loop.run) self._main_loop_thread.start() def _join_main_loop(self): self._main_loop_thread.join() if self.error is not None: raise CredentialsException(self.error.message) def add_u1_credentials(self, user_name, password): self._start_main_loop() account = self._create_account() info = self._get_identity_info(user_name, password) identity = Signon.Identity.new() identity.store_credentials_with_info( info, self._set_credentials_id_to_account, account) self._join_main_loop() return account def _create_account(self): account = self._manager.create_account('ubuntuone') account.set_enabled(True) account.store(self._on_account_created, None) return account def _on_account_created(self, account, error, _): if error: self.error = error self._main_loop.quit() def _get_identity_info(self, user_name, password): info = Signon.IdentityInfo.new() info.set_username(user_name) info.set_caption(user_name) info.set_secret(password, True) return info def _set_credentials_id_to_account(self, identity, id, error, account): if error: self.error = error self._main_loop.quit() account.set_variant('CredentialsId', GLib.Variant('u', id)) account.store(self._process_session, None) def _process_session(self, account, error, _): if error: self.error = error self._main_loop.quit() account_service = Accounts.AccountService.new(account, None) auth_data = account_service.get_auth_data() identity = auth_data.get_credentials_id() method = auth_data.get_method() mechanism = auth_data.get_method() session_data = auth_data.get_parameters() session = Signon.AuthSession.new(identity, method) session.process( session_data, mechanism, self._on_login_processed, None) def _on_login_processed(self, session, reply, error, userdata): if error: self.error = error self._main_loop.quit() def delete_account(self, account): self._start_main_loop() account.delete() account.store(self._on_account_deleted, None) self._join_main_loop() def _on_account_deleted(self, account, error, userdata): if error: self.error = error self._main_loop.quit() unity-scope-click-0.1+14.04.20140417/autopilot/unityclickscope/__init__.py0000644000015301777760000000000012323764232026647 0ustar pbusernogroup00000000000000unity-scope-click-0.1+14.04.20140417/autopilot/unityclickscope/test_fixture_setup.py0000644000015301777760000000366712323764232031103 0ustar pbusernogroup00000000000000# -*- Mode: Python; coding: utf-8; indent-tabs-mode: nil; tab-width: 4 -*- # # Copyright (C) 2014 Canonical Ltd. # # This program is free software; you can redistribute it and/or modify # it under the terms of the GNU General Public License version 3, as published # by the Free Software Foundation. # # This program is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # GNU General Public License for more details. # # You should have received a copy of the GNU General Public License # along with this program. If not, see . import httplib import urlparse import testscenarios import testtools from unityclickscope import fixture_setup class FakeServerRunningTestCase( testscenarios.TestWithScenarios, testtools.TestCase): scenarios = [ ('fake search server', dict( fixture=fixture_setup.FakeSearchServerRunning, request_method='GET', request_path='/api/v1/search')), ('fake download server', dict( fixture=fixture_setup.FakeDownloadServerRunning, request_method='HEAD', request_path='/download/dummy.click')) ] def test_server_should_start_and_stop(self): fake_server = self.fixture() self.addCleanup(self._assert_server_not_running) self.useFixture(fake_server) self.netloc = urlparse.urlparse(fake_server.url).netloc connection = httplib.HTTPConnection(self.netloc) self.addCleanup(connection.close) self._do_request(connection) self.assertEqual(connection.getresponse().status, 200) def _assert_server_not_running(self): connection = httplib.HTTPConnection(self.netloc) self.assertRaises(Exception, self._do_request, connection) def _do_request(self, connection): connection.request(self.request_method, self.request_path) unity-scope-click-0.1+14.04.20140417/autopilot/unityclickscope/test_click_scope.py0000644000015301777760000002033612323764232030443 0ustar pbusernogroup00000000000000# -*- Mode: Python; coding: utf-8; indent-tabs-mode: nil; tab-width: 4 -*- # # Copyright (C) 2013, 2014 Canonical Ltd. # # This program is free software; you can redistribute it and/or modify # it under the terms of the GNU General Public License version 3, as published # by the Free Software Foundation. # # This program is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # GNU General Public License for more details. # # You should have received a copy of the GNU General Public License # along with this program. If not, see . import logging import subprocess import os import dbusmock import fixtures from autopilot.introspection import dbus as autopilot_dbus from autopilot.matchers import Eventually from testtools.matchers import Equals from ubuntuuitoolkit import emulators as toolkit_emulators from unity8 import process_helpers from unity8.shell import ( emulators as unity_emulators, tests as unity_tests ) from unityclickscope import credentials, fake_services, fixture_setup logger = logging.getLogger(__name__) class BaseClickScopeTestCase(dbusmock.DBusTestCase, unity_tests.UnityTestCase): scenarios = [ ('Desktop Nexus 4', dict( app_width=768, app_height=1280, grid_unit_px=18)), ('Desktop Nexus 10', dict(app_width=2560, app_height=1600, grid_unit_px=20)) ] def setUp(self): super(BaseClickScopeTestCase, self).setUp() if os.environ.get('U1_SEARCH_BASE_URL') == 'fake': self._use_fake_server() if os.environ.get('DOWNLOAD_BASE_URL') == 'fake': self._use_fake_download_server() self._use_fake_download_service() unity_proxy = self.launch_unity() process_helpers.unlock_unity(unity_proxy) self.dash = self.main_window.get_dash() self.scope = self.dash.get_scope('applications') def _use_fake_server(self): fake_search_server = fixture_setup.FakeSearchServerRunning() self.useFixture(fake_search_server) self.useFixture(fixtures.EnvironmentVariable( 'U1_SEARCH_BASE_URL', newvalue=fake_search_server.url)) self._restart_scope() def _use_fake_download_server(self): fake_download_server = fixture_setup.FakeDownloadServerRunning() self.useFixture(fake_download_server) self.useFixture(fixtures.EnvironmentVariable( 'DOWNLOAD_BASE_URL', newvalue=fake_download_server.url)) def _use_fake_download_service(self): self._spawn_fake_downloader() dbus_connection = self.get_dbus(system_bus=False) fake_download_service = fake_services.FakeDownloadService( dbus_connection) fake_download_service.add_method( 'getAllDownloadsWithMetadata', return_value=[]) download_object_path = '/com/canonical/applications/download/test' fake_download_service.add_method( 'createDownload', return_value=download_object_path) fake_download_service.add_download_object(download_object_path) def _spawn_fake_downloader(self): download_manager_mock = self.spawn_server( 'com.canonical.applications.Downloader', '/', 'com.canonical.applications.DownloadManager', system_bus=False, stdout=subprocess.PIPE) self.addCleanup(self._terminate_process, download_manager_mock) def _terminate_process(self, dbus_mock): dbus_mock.terminate() dbus_mock.wait() def _restart_scope(self): logging.info('Restarting click scope.') os.system('pkill click-scope') os.system( "dpkg-architecture -c " "'/usr/lib/$DEB_HOST_MULTIARCH//unity-scope-click/click-scope' &") def _unlock_screen(self): self.main_window.get_greeter().swipe() def open_scope(self): self.dash.open_scope('applications') self.scope.isCurrent.wait_for(True) def open_app_preview(self, name): self.search(name) icon = self.scope.wait_select_single('Tile', text=name) pointing_device = toolkit_emulators.get_pointing_device() pointing_device.click_object(icon) preview = self.dash.wait_select_single(AppPreview) preview.showProcessingAction.wait_for(False) return preview def search(self, query): # TODO move this to the unity8 main view emulator. # --elopio - 2013-12-27 search_box = self._proxy.select_single("SearchIndicator") self.touch.tap_object(search_box) self.keyboard.type(query) class TestCaseWithHomeScopeOpen(BaseClickScopeTestCase): def test_open_scope_scrolling(self): self.assertFalse(self.scope.isCurrent) self.dash.open_scope('applications') self.assertThat(self.scope.isCurrent, Eventually(Equals(True))) class TestCaseWithClickScopeOpen(BaseClickScopeTestCase): def setUp(self): super(TestCaseWithClickScopeOpen, self).setUp() self.open_scope() def test_search_available_app(self): self.search('Shorts') self.scope.wait_select_single('Tile', text='Shorts') def test_open_app_preview(self): expected_details = dict( title='Shorts', publisher='Ubuntu Click Loader') preview = self.open_app_preview('Shorts') details = preview.get_details() self.assertEqual(details, expected_details) def test_install_without_credentials(self): preview = self.open_app_preview('Shorts') preview.install() error = self.dash.wait_select_single(DashPreview) details = error.get_details() self.assertEqual('Login Error', details.get('title')) class ClickScopeTestCaseWithCredentials(BaseClickScopeTestCase): def setUp(self): self.add_u1_credentials() super(ClickScopeTestCaseWithCredentials, self).setUp() self.open_scope() self.preview = self.open_app_preview('Shorts') def add_u1_credentials(self): account_manager = credentials.AccountManager() account = account_manager.add_u1_credentials( 'dummy@example.com', 'dummy') self.addCleanup(account_manager.delete_account, account) def test_install_with_credentials_must_start_download(self): self.assertFalse(self.preview.is_progress_bar_visible()) self.preview.install() self.assertThat( self.preview.is_progress_bar_visible, Eventually(Equals(True))) class Preview(object): def get_details(self): """Return the details of the open preview.""" title = self.select_single('Label', objectName='titleLabel').text subtitle = self.select_single( 'Label', objectName='subtitleLabel').text # The description label doesn't have an object name. Reported as bug # http://pad.lv/1269114 -- elopio - 2014-1-14 # description = self.select_single( # 'Label', objectName='descriptionLabel').text # TODO check screenshots, icon, rating and reviews. return dict(title=title, subtitle=subtitle) # TODO move this to unity. --elopio - 2014-1-22 class DashPreview(unity_emulators.UnityEmulatorBase, Preview): """Autopilot emulator for the generic preview.""" # TODO move this to unity. --elopio - 2014-1-14 class AppPreview(unity_emulators.UnityEmulatorBase, Preview): """Autopilot emulator for the application preview.""" def get_details(self): """Return the details of the application whose preview is open.""" details = super(AppPreview, self).get_details() return dict( title=details.get('title'), publisher=details.get('subtitle')) def install(self): install_button = self.select_single('Button', objectName='button0') if install_button.text != 'Install': raise unity_emulators.UnityEmulatorException( 'Install button not found.') self.pointing_device.click_object(install_button) self.implicitHeight.wait_for(0) def is_progress_bar_visible(self): try: self.select_single('ProgressBar', objectName='progressBar') return True except autopilot_dbus.StateNotFoundError: return False unity-scope-click-0.1+14.04.20140417/autopilot/unityclickscope/fake_servers.py0000644000015301777760000001352312323764232027605 0ustar pbusernogroup00000000000000# -*- Mode: Python; coding: utf-8; indent-tabs-mode: nil; tab-width: 4 -*- # # Copyright (C) 2013, 2014 Canonical Ltd. # # This program is free software; you can redistribute it and/or modify # it under the terms of the GNU General Public License version 3, as published # by the Free Software Foundation. # # This program is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # GNU General Public License for more details. # # You should have received a copy of the GNU General Public License # along with this program. If not, see . import BaseHTTPServer import copy import json import os import tempfile import urlparse class BaseFakeHTTPRequestHandler(BaseHTTPServer.BaseHTTPRequestHandler): def send_json_reply(self, code, reply_json): self.send_response(code) self.send_header('Content-Type', 'application/json') self.end_headers() self.wfile.write(reply_json) def send_file(self, file_path, send_body=True, extra_headers={}): with open(file_path) as file_: data = file_.read() self.send_response(200) self.send_header('Content-Length', str(len(data))) for key, value in extra_headers.iteritems(): self.send_header(key, value) self.end_headers() if send_body: self.wfile.write(data) def send_file_headers(self, file_path, extra_headers={}): self.send_file(file_path, send_body=False, extra_headers=extra_headers) class FakeSearchServer(BaseHTTPServer.HTTPServer, object): def __init__(self, server_address): super(FakeSearchServer, self).__init__( server_address, FakeSearchRequestHandler) class FakeSearchRequestHandler(BaseFakeHTTPRequestHandler): _SEARCH_PATH = '/api/v1/search' _FAKE_SEARCH_RESPONSE_DICT = [ { 'resource_url': 'https://TODO/api/v1/package/com.ubuntu.shorts', 'icon_url': '{U1_SEARCH_BASE_URL}extra/shorts.png', 'price': 0.0, 'name': 'com.ubuntu.shorts', 'title': 'Shorts' } ] _FAKE_SHORTS_DETAILS_DICT = { 'website': 'https://launchpad.net/ubuntu-rssreader-app', 'description': ( 'Shorts is an rssreader application\n' 'Shorts is an rss reader application that allows you to easily ' 'search for new feeds.'), 'price': 0.0, 'framework': ["ubuntu-sdk-13.10"], 'terms_of_service': '', 'prices': {'USD': 0.0}, 'screenshot_url': 'https://TODO/shorts0.png', 'date_published': '2013-10-16T15:58:52.469000', 'publisher': 'Ubuntu Click Loader', 'name': 'com.ubuntu.shorts', 'license': 'GNU GPL v3', 'changelog': 'Test fixes', 'support_url': 'mailto:ubuntu-touch-coreapps@lists.launchpad.net', 'icon_url': 'https://TODO/shorts.png', 'title': 'Shorts', 'binary_filesize': 164944, 'download_url': ( '{DOWNLOAD_BASE_URL}download/shorts-dummy.click'), 'click_version': '0.1', 'developer_name': 'Ubuntu Click Loader', 'version': '0.2.152', 'company_name': '', 'keywords': ['shorts', 'rss', 'news'], 'screenshot_urls': [ 'https://TODO/shorts0.png', 'https://TODO/shorts1.png' ], 'architecture': ['all'] } _FAKE_DETAILS = { 'com.ubuntu.shorts': _FAKE_SHORTS_DETAILS_DICT } def do_GET(self): parsed_path = urlparse.urlparse(self.path) if parsed_path.path.startswith(self._SEARCH_PATH): self.send_json_reply(200, self._get_fake_search_response()) elif parsed_path.path.startswith('/extra/'): self.send_file(parsed_path.path[1:]) elif parsed_path.path.startswith('/api/v1/package/'): package = parsed_path.path[16:] self.send_package_details(package) else: raise NotImplementedError(self.path) def _get_fake_search_response(self): fake_search_response = copy.deepcopy(self._FAKE_SEARCH_RESPONSE_DICT) for result in fake_search_response: result['icon_url'] = result['icon_url'].format( U1_SEARCH_BASE_URL=os.environ.get('U1_SEARCH_BASE_URL')) return json.dumps(fake_search_response) def send_package_details(self, package): details = copy.deepcopy(self._FAKE_DETAILS.get(package, None)) if details is not None: details['download_url'] = details['download_url'].format( DOWNLOAD_BASE_URL=os.environ.get('DOWNLOAD_BASE_URL')) self.send_json_reply( 200, json.dumps(details)) else: raise NotImplementedError(package) class FakeDownloadServer(BaseHTTPServer.HTTPServer, object): def __init__(self, server_address): super(FakeDownloadServer, self).__init__( server_address, FakeDownloadRequestHandler) class FakeDownloadRequestHandler(BaseFakeHTTPRequestHandler): def do_HEAD(self): parsed_path = urlparse.urlparse(self.path) if parsed_path.path.startswith('/download/'): self._send_dummy_file_headers(parsed_path.path[10:]) else: raise NotImplementedError(self.path) def _send_dummy_file_headers(self, name): dummy_file_path = self._make_dummy_file(name) self.send_file_headers( dummy_file_path, extra_headers={'X-Click-Token': 'dummy'}) os.remove(dummy_file_path) def _make_dummy_file(self, name): dummy_file = tempfile.NamedTemporaryFile( prefix='dummy', suffix='.click', delete=False) dummy_file.write('Dummy click file.') dummy_file.write(name) dummy_file.close() return dummy_file.name unity-scope-click-0.1+14.04.20140417/autopilot/CMakeLists.txt0000644000015301777760000000114012323764232024074 0ustar pbusernogroup00000000000000set (Python_ADDITIONAL_VERSIONS 2.7) find_package (PythonInterp) set (AUTOPILOT_DIR unityclickscope) execute_process ( COMMAND ${PYTHON_EXECUTABLE} -c "from distutils import sysconfig; print sysconfig.get_python_lib()" COMMAND sed -r -e "s|/usr/(local/)?||g" OUTPUT_VARIABLE PYTHON_PACKAGE_DIR OUTPUT_STRIP_TRAILING_WHITESPACE) install ( DIRECTORY ${AUTOPILOT_DIR} DESTINATION ${PYTHON_PACKAGE_DIR} ) add_custom_target(test-click-scope-autopilot-local COMMAND U1_SEARCH_BASE_URL=fake DOWNLOAD_BASE_URL=fake autopilot run ${AUTOPILOT_DIR} WORKING_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR} ) unity-scope-click-0.1+14.04.20140417/data/0000755000015301777760000000000012323764741020236 5ustar pbusernogroup00000000000000unity-scope-click-0.1+14.04.20140417/data/clickscope-screenshot.jpg0000644000015301777760000010350712323764232025233 0ustar pbusernogroup00000000000000JFIFHHC  % !###&)&")"#"C """""""""""""""""""""""""""""""""""""""""""""""""""@@ LTApKu`,d5ͣ pJ6DhA @ .#V`*tiF$I2c2Dt G@R 2 A$ J$ (" u 2N/fe$HP#NRY2k2- *\B( BX@,@Ҋ$[Alj4%!Fhe 狙bYZe{֋VWw,~7WnmՓNnCm?fP0r^mɎ$|vo˟tPP.~jŔJg2#y};ݯͳu>Дaop[ǫ'͞(d s{[,}ydyRޔU~egt5E]1x"c9i@yfj 8=_.gc^>>W_~@WK;N~V]_)ߪ/O(1˭ɳg,0Ll<VB1Bg>ˇM= 7نww;@=vgeْmkKIEIbAP @$PqMe 3&EX5JFK2@@(P/1$cڙӠ2ܥ^5@X&4 F ׉,A,Xэ1Ab4#V6`=&SbP,1S e6NuHX,`@IX$(P*3V#z=doYǖV._{р <6|hgbx5ͳL5`pO>t3yg}/lm//'g玼=|2<-Lƹe7s(9g}~͕2e?>k&]ZxϧH$ `A џl l AS|=<1菶| lj|r\2~ yg;:Zvv~3qvpsϐ&sݝ>"m5Ϲ@bHvϵ@ DH*I6=9s 0y !쎰**d9ffgV]܎k@`6c5j*dAHu VcR^B5Y ΞSJU1<]\y H0 =/oN{ի>,^8K)>z_}c0r6/<3LԂR ,/ַj7'7?;w^gǃݟ)×'7}O&;9]K.vgɻԂd/zkoV_۳.~޽0')ڶi]^gi 8y;|to}7?ٯy+}A_"}*[WwG;ao+b/O6z>}>?E=` \poӟ>8O}*dW㞷Crt]xtu71cu<~ꯗkJs} mOXW>y-ǯ.|Z9DS slc'_#Cya.&_ن.gdwbVC%LiNOo0 2!#345P1@"$0AD9x~p1!/9LfM:۴!*w%+&-PݍV̻Ĩn!FާKE_7 .N-1ܳğěv5pӌaĈ"?*f O2 j$p~8IF!F_/UVWP*]AkuUJPZ ]@uU WPZ]DuU WP*]DuP*]Ak-$5;Wpun 7\pun 7\pun 7\pun 7\pun 7\ `ۚ 7GNsL?ϱ .%A**"ix?N^u~uu(^Wر}M_|LU1WCd4%mjξ2k kլŖư/ AGP8d,{]F&b=p_GtdCqmi֏u]=$HRjk?_.]|Jݰ/ VTևkCxcmvQlqIJy*TtJn 2C$#ÍOnw7l^G"Ŏ6*:j-4뛊_x~n_ƺ1mxVy ^cVNx`tAx\A @%*% nmBRҎyʆQ> ܽ LWIY-feI/GYhW"[U9,HeW:n\Tn ù*wTCp+xxҳ~op\$ۭRof7n5[ჟ¢*m7[M֒Dk$dkTE7Z**pIx~M8;pȹ  ޲Zk%6.CFDxIYuIX~$–@=u9n6[|[qnT.9kRļ?N^I'Wt(,PCM>Ӆ ܜ: [ 5&=RF@#vfroQlmtc,tָm<q]F) /sVm ѭV"4axbȃm#4Re'dF."UXJitsL"2PG*6"Տ誫]ak~2}(3mGഁ4 t?N^8.nbC4(]HRf5.mF2GUN{ҫvUnʭTR_ br=@寉^^^^;V˳w!0|#,Z嬐[t;Dp>+lmp1h3.A7:n 0Ӧ^J0o]:TGR⍪i!o1M]Ga8n#tbN)tźV>~~ᄟ24)PБ\ܹvMTf$'* gqc/ ޛ/TK}+/ $/]5:lP'vMU&#F 3L}%p>r5'^I1E/GED{J;B4Y~/{jx.?^'Og׶?ɵRm??MO@GMrA"i* R%QG?SJ UhCSo-&c>^n9IDvIz{ )m- iEʎգV QEebq+_ µ|+I_xV`7x~1 @3BCYz(3k;lvnFݓ6rbr&*XVhp/ V3"qՎq5#Ъ(9رQfE%8i"0#pu=^Wcn -rC %U,g!m28c22dm(_sOLD&ˎmPtq%t?N^/ Zh5k@ցZ h5k@ցZ h5k@ցZ h5k@ցZ h5k@9x|tJq NW Sok:Hl+6GB59uքCN' r\"-kav>4j^FE\wkm>4'ͽw]iNZvEz`^4:[u?˱}\}d^O99rTXKsԘv%YTm\ ihW'h߻H35ݔy~ETY_dPprѻ>蔆&Xmq/˃u0?*b& E6yǭ2q]J:7ؾ>i^ x!c9Y[dƁiXdrYUR/XWe5rs0s8˭SBfl%r<m'?_YEk+[N[:u97X_]TE{&:Ӓye6kZxَ A&`'#[J&{\dj}UǙ*숐-OYZ#v:=IZ%jJԕ*Ȼ`^\^#vIEEʵpZYp46}c5|)_ W•'@fXǵ& 8ĕY:_ B|#&& L<@Mt9$fHA0L k+xmцY=g' Ɩ+B|B!v\ӹ8 M4=H D!&-3xN^?/ӗ3.8S*Y.RvNKq(n.[ +#zW 8lifӆ=*\Vib\VkkMEjšUMP6uo=.o7I,.:@b`wS}o!JJҵ:(tx/p('n#y,n e:8HhdM}rKmfU)R/pC3AI\'5 "PU,cnj7m:5Ǖ" 2.! /&}#Cem1})tY0ވ-[m͒idY!#uT6H$d M-_^8JLҭCi%.3xA4ueA_GvH]TUn1C F:/}\r"JTN$RrWb񁶎 WSv*e`hװPٰ֪ܤrֵ-['d^?7 !1AQ"02@q3aBPRb#`p?d?."."." k ._ W677 шu\Ӻ䃐4 P]+duf,uX -B@ 07 <ei %-mBVGi&%bkԦjSBkKCvAEL.B:9Bd a5_/ ~ IayUUVT|UCl4)(He n;>'(QS=ʪ5Ea(&PCl<L:*)pH5 %!'˜U6T 0ek5 PQ3d.D}ОH;?l/ šι uBWuBS R̍Z8 AO\9{Y.kUܮkGc꾾'Ԝ\<:u\+׺.A;tqARJ0#ړ/_i̾ә7-ќ DhݠَjKı*J^Yb㷽]jijq4z>P*Ł5]oaqBW{$츭;9݁|=S-tѪCa45u7fve6f#fScϫ3n7Gڿ#MƁEkEn70NAǣ4V'FɃL9ЃG-QVa XB25K 5g0щkڜ-Yhֹ!92Np,+ ©-XV~N+"cPpREnǩղPB`h`Z2YF0kV!TPj°TQyߨUQ`5wa$G"EqusDK TS(_Zd3NcX("ʄY QÙNzJ-S3i}UwN3$!Y,8CdpQ3ijU'Tug[&Uh6a(l oEl"C5ڲHn&=.]l{£mqƹ1P l08Q64ɃCPiad59,{kZ-?23{pQR&djݿ"gòs㢕w*]_x.U}t7;wKT-BPJZ|#w>5ҫCrE}产~/<mvAr"&_WyWyP J[cl{u?'mv?SUӵfhjjw[‰:M!?[ҹ*eau]~w[\P9?_$;i ?(ScE- v5.EOJN9;[G;1ȄZO0R T_C~~ۭ)Q UF?HM6UWtN57EۺE pPiF;c1BUlL7rG4b4MhuAh@Q`miۗV#r){S4rgTS @xtP!T6E=Zݖ۲v[nmm'8)k~/T2P wkgD8{S*.d5V[$4\.ɹ!֨eNɹ2d9!.Ț\ OM .e ?pEƙgՆEsi:4LlvM:)&i@&Z9I5[+!.i=iNki9:w>z,ӋYQMs>HG\<i;D$삧$3-.UTFHeNdBG3U}*T2odTsTsr(,v-:-:-:-j2g5XV"e nUUU]PtaVnM/vaIiP : V0rS87ҡ)NQ'+2yAU%AdhUSI1=AQ.ɂ粙¢pi>aJd' ME}$*)U6[nݵdM-Z\fcbvao3_7 ͷw_S\-VPhS$qYm/^(1pTfaF.)MQ4RJ>!!|b c_7 TUV<9pXÅVpfLM*>1[/<ޑFWpЯ*O5A;jHQUT՝]E !1 "2Aq3Qa#4BRrs0@PbCS$c?r!Z'\WY0f=%y7 J"itegS(&p;R0&=sɬYk.S'qVhmAHL Me`3P8Mecjm?9blj+TJ4#պd}VjKHLi`N y(-hYB#VD!cpd +[Ȩ/ig" d[|Q^\zl,GR d-M?U஑Dr5c/ChFkq,#䮹Mwd'ge:MxT'Fۚ4u1Y<2&`V`V`s&/#(RD5 ?G0{%]f*t*N<:'.zihxLUp,U;S9~N^AV{rg5.7pO[DpBwM8'Nq!3yxM9k&˂K\"T`KE}D1SPCcݩ܁ * 6-oD{2 NK9 MT ϘBss#a)"z8b8DŽTg%[K +zS+L? e/8~CeTH ߞQ^ih"7#s5斏C=΋]5T\֌/P :-IA6;䙘sfK]H~wjVch:cn|2;xeh|1S,F#\Zs鉸:-kaIh'cujY!#A޻Z=Ap1ǦOC8uY] ԝ?e<2 yȟ|Stw x/58h罕ÚzʄwКXpVzh?l]&|6F#]Oh) VOt))7{wBQdGҮW*%B~Y!`2d';{cyH~wjVcV_d<]+_4ik9{Үf_Xo^w~+?oy;⵭wtCT\q9O ٸf斨R\#d'7Rͧs'ۋY:nsZdkX'$Xnc..Gk.|N1k67(5Y2lRKioB1ѾhZZdAܳ‡;(54*4Ⱥz#5]%5Z3(2DmO&Cqs̩@:i cPnvKG\rمM\f9scqtq MAg(3M钩ՉDkj|:q6=3~)e@H'jJ@ZkLJl:[#l:[!l[!l[!l:[!l[#l[!l:[#l[#lFA:IIB' [[䙛3ND Zc㖙|S&DQ2'sඌ9NH??0[9bN pj d/8=Bwpv6rĕ|P85y\s!RK# <&rlK[oE}}$Ģ)(ŧ)PlwN@"֋(>yG\\=Vʩљz?v!oF#ǢQZ=khֶZ=kh)8+;gdznM}a2gfS8c@~ ,Halr=w}ӻp-}xyJ%dJd[;|P&){HF͑{9_}s hl6ٛć( |:xf @ j(64 0A#DCx$G8OSԈۦ9vEͥ:5C:=&(1Ƙ ֍j,3ph4E{f: `[ ;#fesΆ_D$p{?qR3sVoz)Hw>:A^h͈¢ ۝CųmLuSНgef75qTYa7췉D4q914q.snXXXN3Qt'W9ZjŝkYz#CqbuA-%S,l'wz ֋W~ߊ^y^+?okZ⩂/8rI;yVs  |"9UfK- 5EŴbH+݊ք*:yNMSB*wc9 16S6Y5>ȱ-2!UAkwO~S0YL{/@:$M(>C]VE--ef ,] u[)IED։+pEL9b4XOi|Fb\첃v*Sa4^[*͐NkELdHA,^֯G##+G L>Xq+ `-B*;HnF%kMJ `-n{8f}s9b;e|$S?b4qw:-0$dnܨl.$b™ Oc 1SY b\T8[EӄGݖ/="XP%7Er.oB1LJ˱Tw(nmGlMaq:uȘsjK71)hNI{Z9ɒ04SݿZ"v~;_#vg2vmB̉oUk-l&5{9mK?o~u%j@#Cxޤ뤱>cF/rFhbuw'fa Dco =m>'\M|.0C)hnP*2{]fsQlVӹoY@!ΦʔV5̊dϠ_`YPAsoڇf-ih*aG=o}Pߔ9{,keS؈13P*DHq.2w x( Wd O_`dphj.w9W Nvϓ|F`yΔK֯k֯^AAޜțWdb/R*!1AQaq @P0?!B8׉ؿ( 4Fr2H!8+7o&j{O=ڞOiS}>j{O=ڞOi}>j{O=۞iS}>j{O=۞OiS}>Z{QTdc2&aKR9L›Ô/o%s-CX{nAY@P0ޔbӐU֥^p]3ٿ]8N;*֘{|?i~9Ly.cCa2gϕ+69~QxxYa0r_`\9Cƒp^VR9Ԭ"ܤ"ln{ 8@)ع2yʘ%!n0Jө2`9@W~SH-Ϣ ^߭o<- {ܟ,7.4dPǡ9OIz@ܷ2w1;Fo'r#,šG M_Hy*.=ɓ)' ^x2YQʀqAǐ]zw@*42sTɷzP<;{g~t|7/on)u4{-T|`5( p 9eKfwAWR8[[d XS)5MPŠ7, z<{{߃ީ<kՂq$Vi6LNmA0)k.QL0bs'+DwklnCXĽ͐&ha3XK@\jg8XK=\<3({{ߓ$}~ XP1g.tsYcuvO)˜sVL!^#EҵEԴs0z@ [{!KD2 |ϭ < -͐#fBe9n̹jP-t!wg^hЃ*(xyZXj xBx(+m[.-9+'Nc 0uW Yn4\;K.AQW9Ƅo?ь'7޾o8@/]`Q巽mׂ.L+z\\ nڦr,XO9k-0!28Jj<ߘ R.$*#Drjo0&zdJY+%8vQPf MkRJA/yqKKŅz3Խ%bhߏx fZQ}DJ%D&w"f8Ԕȅ;AAX780MK*i@s(Ki`%EX7֠ j\x B%x>5k)L. l[6Ֆ0+س̗]8h ŞUf7LИ\@q:8̨&$0b|)+lsT4'U@sjVw_tDpt~x.tx<9Gd`Xպ"PBxNF(+Y p_13 a-^?Ota.SsNOŧ%q=6lٕr? lqn&+y|)b!0EXLKTc#1KǬ x[S.Z2*iҽ%H5\MA`|">Ij0-Me7/m|f6~!t\>XJ|Ǧ\Feh/̕i^>'Lh.<ͮ k|.QgI|7p?;NSzRmozۍLFigIRֹ%jw\UtaO çH `ots)!tR[IL5k+VrWu<|1|2'P Tl7S=zhg6;xM2z;`C}N%|)JJUœm?Tɮ(˜q(A?װln>v5hʂߜ߱Štw;LcfS8Y%s++Q9IN麵H-e|J30 /V ywWBYhru<|1;wOjz8Jۆ= K!NԆ27a-ݝ]>U𵻄'4sFjzk;Php0;`{pKtx $ Z؂H4R17P;ub(0;v:lmV{L%y?SQWc{ã2Mw  ץTGz_P;A nͯ{ߘm2*Cum!޿0#:]f}ʥJ v@ hgJymipsl@d%QjD\FB%]R8"Y$8AZ̗55֐Y,j3s~[,JzIRk20H,en޼`.nUɆdMٚks([U+sTۜ"s`O#`mmjYDr7 e|k9y޴Ҡk7/jZSL@ +N5 W߷xI!ul*dpÌ,Ց#uwl` 5Ckq,0%_͎Vr/&4 7?޿]D)&},Yv gϡ JYD/C>h;|`p0ԣ9=eqغk-d#xhY(cK̕:@ԫؖx+sXkTĬҩL$*w4ʀM8iEسi+^CD|>iO~鞿edzĴn'%O~?S៨E~NJ8+OGT\\"J?B tŵ^8bj0mJy>h!0֊oB^F#b/xK>= 9r< j<>%jRPv͹qvI|YpЍryIJ:'jQ`x275¾#c.}q#bi 7p($ŋJۘHwGgٷvZuiFE֞ꔰ- zRKws={=y9MY9/\Xo<ij-Y8n͊\0s3n;w;5̴Za,;{o~\SJ,ss%kU%Օ/oƜuyk2^\zIJ/M+Gbgҕ$ {E6ߙ⊵oV|P-xL3*u`B˟yEZ-Y`ʪHЈrwT+pkv8.;>M"x۪p hҨ |@c"KV֝ Gx{H ~RB Ѕ8&z#Ty -mzƚΰpiUvԴɕ'T @ ¿K#Ô m{`^:ǁ !9svogpm]c`sl<tӚ6G\9BuĊy-%zw HzҬO_bqP~XJ_)Om.MWok(i/}%GuZ':e<[FO@>%G5:Id)t҇JFK|;uy)W!]+\[tڻ\ r>Q(QmLUG]2C\[XC(݅f6ϕ t;{=.ZZ겁b+t=xJr~vŵf<= Ȕ( _ 0ẙX< )3E@#tF-3:ʜJ_Y{=9űDPy,(n{Ԟ  s%,ɞfd}ˍ-frUtJ#8n )\V|?=SP@)-L.V9njV=b {/o)qCQJڛpsb-j:M"1RLA D8ѵϒLwMa "e@ 'KrLu%esbT @$@` I 'n$|* @$0H q$Y~ zH$dII$@r]<mAd/c'@w <2@L6.P@ &$$}R@˲m @2S#o [#G$>n #!K$ A$A IS<h T $?z@L@$0$ \ $I p$RI$@ $I I$A$I$I$ $AA$$DhI$I$EIC@ $ $I4I$@ I  IA$I  $ J$HH$H 4 bC I =CI  @$AA @H $I AI %I I$@ L rJ^I $ H$I$@ $I$A$@ $X I%H$\ߗHHvQJ_$cn-V]4%`d< Yz9Q TNI]$񱔒J]$VÁHF-!1A Qaq0P@`p?*ȞsD(ouR*WJr))SAppMDL#OH]*-U@0(-YÆX6p,}"EpXe㺫:|hٽGg& %Kc;:ve~Xqf!1 YqU8f#se GiSv5 (űvH:fK2p,ÿܥ/NZU*YM_\DOYKAhrK2,fU&u[XNe+W(@mMc|p%RX+׬3}!!gH?y>(hV;˰^Tˢ}@z Th5+M7 ?4P)w5kWYr%ˎ3ahU?1漐iD:|M?]]bP`^Γ4^d_g;Vw[箙V-v@?W~/@:i}NcuSP7$(4x<$[~- MF.SW) DG:6瘋@j/kÿ3h|K@I zP(.ͺi"Q:n9pg#kwik|><{DigOnoߎlg#k%Lji! 1_oe;36/>Ku |MF}AӿWӮ><5Y8]`WX"-xk A)3jA?US4Q~%(y倶Ն8+~q!zK:{GfXіkU.jqa9/N `ϦҐ([+c-_QrW3vd<w"i={%So/P+^Aa5`ro+ՉQW1]=ZÈ?qV*iURJD]RSy96\$m2CkO~ Z)3.t4a}r@䯼ͳA;&hp7/T_JVXZFBO`g-_'uUVSAعr[֥XS]d`鯣d[itG&D_4w\?9k3{*cZ-_XNġW.h_L ˎ7b3EwRݨe+ve9f2ިŗ. Q+7TQy0i l߼Mfph<hp|·|63u (`VZS+lyKe!hAEM"ZNq *ZpŢXJ]6̾ e+ɘGAԌßxJ*Xò5 cyp ./ H/h#]tS |0Uo;.\rC::Mw $gcba EUm($Y %5æ&W7濫+d'3+W4n;h{ X֭+:45wi^6-#&Tc]^/xս& \Ώ漌fw}&8Wƈˇ6'{`Uw|uvG^߈U 0u8mjZħJ}r8,jf5@&|ZQ,.zk-mf M.>pYz㜡CB.&(·܋PIN+Tb:zl̨Pr J A\#]^\2(ob 4X䮳}i+ЯV54x Nؕ">^J%F` -R2sjՠV9&%a\2Th$$"LTR.ZN_ӫ/{9.G܉R x!y"$S`R[Mmr+~,yA/}"Y]OѼѬBM!6qA SS%]Yy|Ju5 jI}X Wc&Eww%vOy Qk*u#3Z۞gw8|n8W@sVa)kᗐ|pC…#yxp>(V}Nu/FE0nbS3*ha6S@޷>8{i 'aY[LG8t| &( %+?2M @z!8ܕϙZv8{i42!ZM<ccA4u_CHrq:LhJ'iQm<4|idr|Uq7N~E~2 v7{n"+iKo9{DPuzA7^,^Xm14|+XR u)ieSU9dXێy/,6ߥkyB,ܭ/%q3_o3ZC"pʮG[ .W‡ l*ܺlU6r1Aa y75CzL.l5y/M@h}b@n\VbPsOyc,6KU-]һ4^i91HfC4j8IK5t +_ʋyB9)z/zW̍LNz} fnRùA2{G$ YweyV~W"aaյEp&L+^f | g. W, luQ;ӫ+~iԲ`Ox6<}f_$ݨj;SiC. acm_`cu _[姩5y 5Ab?1'ް}lw Fs&+pc 2ŭʊ@{Lܩs*`w%G!̩r5M@sR}ۼ_E'.6<؎yQ xDSŹjT©"*n'& <ʷ~G٣ыGݣ?Gݣ}>vGݣh}>~Gݣ9"u[鼾֣ S [_[^ 0"@*@$H @$H"D,H"@1x!@X g=[_ᯀ=/ezy ƃRkjMoYb1 +bsjc */&-1Sx.U1  fKYyZCU.QL-.`nSPv?yEa?eصaHf;#8Y_yE.іC1TPņH ex"B tudfe=GrԆLo3X_qAwɒ-'ȖvbCik05, ̳q#3^;@?.obfa{Kq)7Pq>#JMX:{\KoX&ӈ +[0I.׆6O8B5;x]P3knt`TqsW)2\t`Rp[ WK,ʇ6ꔎ1Kij +^=ïqy=g^qN\S@=F9.t1ǀVv5!a*AlesdvZ@c͖=#=5jpRS/p09ԅ-1s0b # qsMG @"t,@&3gOϺϲϲA(}/ =5n$D)@8t<uFNJ?J@@A*.NTלjݰf"Q33&hAR<bU@0"گ3I:[㙧ח^;u:g]@$lUZYZ1Jx: 3Z楆4;rϬoadc"ih+`U*֊b ֽg4Eham_J^^i%raJZJP A/j(dTDڔlj[Fs D KGf@pmdŅC#C;m*`5Vyt,7H"*A*҂| ~3 u,zeyfhuo鯃8h/?=il:;0ћ3 B6*JYaman,EZ#ADib!mty0x^`5`&h9W^O Tvɝo{=;ar B}#Ju/hѴnyAAVe5J/uOJb-;J]ˠ.o4.^9.(`}uNx'Z]k}Q"4sh^\EpRg!,Ud},9m[X3%g!| EDՕ4Dk2EF!V=";FT.|' ZB GWH4^EȇC"z&2 TLi@&P(YZ+lѩF3Ӥ%)Z+ Sx.^>a.˭`V ?[<+SK;O .|M"eZXVEUO h8U"߆.!lY,N@,V/whiqB+Y&f^䟙%O*,j^.F[f0*)XAʜV@ӧO㥏0`+0sXaΉ;;>#ub^V*}LFFd%-dŸ89~P*$o tY 8 z۠nYlh=͈`i/X%PгƃՕӏu|mpM~u*> lv #q/Jrl)JjH2RFCȏr1R ,IF@#Bdw G_CDyJOD*5WQ $P V0Ҍ8mUȊS)=mp6v*4*$+dqo 1ч¾ |W||?L{|G3]_㫵Beib`)Cc-IZ܎iH зj`4]bzlEaJ0*(JR.~^m1 GT8a ޵wT2wCx\bOpU^ U3 U|ZGT?i҇$u4mU8#PW#p hVjGdvs8AS%)K&sRZV[}bhRRE+X)UiJL] p@(%#9cr(TDkEF>jJ4҈x"ɡ(.2L ]y߄YߌBDWz>RJ+ YWJVνG/]7Kg1EG=k$oeB EU Zf=,H7u_U"Mq ݝ:9p,HۈZFКaEa(KF0-i~rʑz-!iKn#Zr ~c5ATn33Zd_Cl(AU-m𶮥آl0.<9Zw;[ W)ʳ+fgR?N1u̪LjV@)-lBW byJz(0ޢVzl޸xp̫ \w.VVӪ"V掲Ts~гKW{v+:JLD4" w\F#Jۡ~M|9s丁mJNf8Vp/U=__8#Ra벃N;yr$VnM ~դ w7 yxU^}F R%|>z.ë\v n}LSԁ\6w "LF0u–tDZđ.4ĩ-I1᭵Vkh@-h&|W0^F=XMé[[dڂSə6U` [L"1НfJ{2XzMz՛7E\Ubx77Ȥ!m WF1R"4B^T&zY}ijY^@h<Ÿ1^.b>g&{ᷕ(%q넩sp쳕er0'\~eG m*jm ۦS⮣립oo._Gtz3nx_߫6=<\f.^wa'hXX!~W&?kŹYWGsKf?G^5{#ŵ&!Ά>̮D{]0RHc-*[⡞V`W \Fck0AZ=;A[+-Z aIKhAqĕx8 }yS! b֏Z!G4FDw<һ;kWsybZNfu j1`yO㤿];BAt&zWB#jDxP=Y*w`i, FAk5UKąWp(jplEz%.#=v;O;hOunity-scope-click-0.1+14.04.20140417/data/apps-scope.svg0000644000015301777760000005436112323764232023035 0ustar pbusernogroup00000000000000 image/svg+xml unity-scope-click-0.1+14.04.20140417/data/clickscope.ini.in0000644000015301777760000000040512323764232023455 0ustar pbusernogroup00000000000000[ScopeConfig] DisplayName = Apps Description = Scope for searching the click app store Author = Canonical Ltd. Art = @SCOPE_DATA_DIR@/clickscope-screenshot.jpg Icon = @SCOPE_DATA_DIR@/apps-scope.svg SearchHint = clickscope.SearchHint HotKey = clickscope.HotKey unity-scope-click-0.1+14.04.20140417/data/CMakeLists.txt0000644000015301777760000000036612323764232022776 0ustar pbusernogroup00000000000000configure_file( clickscope.ini.in clickscope.ini ) install( FILES clickscope-screenshot.jpg apps-scope.svg DESTINATION "${SCOPE_DATA_DIR}" ) install( FILES "${CMAKE_CURRENT_BINARY_DIR}/clickscope.ini" DESTINATION "${SCOPE_LIB_DIR}" ) unity-scope-click-0.1+14.04.20140417/scope/0000755000015301777760000000000012323764741020436 5ustar pbusernogroup00000000000000unity-scope-click-0.1+14.04.20140417/scope/tests/0000755000015301777760000000000012323764741021600 5ustar pbusernogroup00000000000000unity-scope-click-0.1+14.04.20140417/scope/tests/test_data.h0000644000015301777760000000312312323764232023713 0ustar pbusernogroup00000000000000/* * Copyright (C) 2014 Canonical Ltd. * * This program is free software: you can redistribute it and/or modify it * under the terms of the GNU General Public License version 3, as published * by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranties of * MERCHANTABILITY, SATISFACTORY QUALITY, or FITNESS FOR A PARTICULAR * PURPOSE. See the GNU General Public License for more details. * * You should have received a copy of the GNU General Public License along * with this program. If not, see . * * In addition, as a special exception, the copyright holders give * permission to link the code of portions of this program with the * OpenSSL library under certain conditions as described in each * individual source file, and distribute linked combinations * including the two. * You must obey the GNU General Public License in all respects * for all of the code used other than OpenSSL. If you modify * file(s) with this exception, you may extend this exception to your * version of the file(s), but you are not obligated to do so. If you * do not wish to do so, delete this exception statement from your * version. If you delete this exception statement from all source * files in the program, then also delete it here. */ #ifndef TEST_DATA_H #define TEST_DATA_H #include namespace testing { const std::string& systemApplicationsDirectoryForTesting(); const std::string& userApplicationsDirectoryForTesting(); } #endif // TEST_DATA_H unity-scope-click-0.1+14.04.20140417/scope/tests/click_interface_tool/0000755000015301777760000000000012323764741025742 5ustar pbusernogroup00000000000000unity-scope-click-0.1+14.04.20140417/scope/tests/click_interface_tool/click_interface_tool.cpp0000644000015301777760000000507512323764232032612 0ustar pbusernogroup00000000000000/* * Copyright (C) 2014 Canonical Ltd. * * This program is free software: you can redistribute it and/or modify it * under the terms of the GNU General Public License version 3, as published * by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranties of * MERCHANTABILITY, SATISFACTORY QUALITY, or FITNESS FOR A PARTICULAR * PURPOSE. See the GNU General Public License for more details. * * You should have received a copy of the GNU General Public License along * with this program. If not, see . * * In addition, as a special exception, the copyright holders give * permission to link the code of portions of this program with the * OpenSSL library under certain conditions as described in each * individual source file, and distribute linked combinations * including the two. * You must obey the GNU General Public License in all respects * for all of the code used other than OpenSSL. If you modify * file(s) with this exception, you may extend this exception to your * version of the file(s), but you are not obligated to do so. If you * do not wish to do so, delete this exception statement from your * version. If you delete this exception statement from all source * files in the program, then also delete it here. */ #include #include #include #include #include #include #include #include int main(int argc, char *argv[]) { QCoreApplication a(argc, argv); QSharedPointer keyFileLocator(new click::KeyFileLocator()); click::Interface ci(keyFileLocator); QTimer timer; timer.setSingleShot(true); QObject::connect(&timer, &QTimer::timeout, [&]() { ci.get_dotdesktop_filename(std::string(argv[1]), [&a] (std::string val, click::ManifestError error){ if (error == click::ManifestError::NoError) { std::cout << " Success, got dotdesktop:" << val << std::endl; } else { std::cout << " Error:" << val << std::endl; } a.quit(); }); } ); timer.start(0); qInstallMessageHandler(0); return a.exec(); } unity-scope-click-0.1+14.04.20140417/scope/tests/click_interface_tool/CMakeLists.txt0000644000015301777760000000046412323764232030501 0ustar pbusernogroup00000000000000set(CLICK_INTERFACE_TOOL_TARGET click_interface_tool) include_directories ( ${CMAKE_SOURCE_DIR}/scope/click ${CMAKE_BINARY_DIR}/scope/click ) add_executable (${CLICK_INTERFACE_TOOL_TARGET} click_interface_tool.cpp ) target_link_libraries (${CLICK_INTERFACE_TOOL_TARGET} ${SCOPE_LIB_UNVERSIONED} ) unity-scope-click-0.1+14.04.20140417/scope/tests/mock_webclient.h0000644000015301777760000000743112323764232024736 0ustar pbusernogroup00000000000000/* * Copyright (C) 2014 Canonical Ltd. * * This program is free software: you can redistribute it and/or modify it * under the terms of the GNU General Public License version 3, as published * by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranties of * MERCHANTABILITY, SATISFACTORY QUALITY, or FITNESS FOR A PARTICULAR * PURPOSE. See the GNU General Public License for more details. * * You should have received a copy of the GNU General Public License along * with this program. If not, see . * * In addition, as a special exception, the copyright holders give * permission to link the code of portions of this program with the * OpenSSL library under certain conditions as described in each * individual source file, and distribute linked combinations * including the two. * You must obey the GNU General Public License in all respects * for all of the code used other than OpenSSL. If you modify * file(s) with this exception, you may extend this exception to your * version of the file(s), but you are not obligated to do so. If you * do not wish to do so, delete this exception statement from your * version. If you delete this exception statement from all source * files in the program, then also delete it here. */ #ifndef MOCK_WEBCLIENT_H #define MOCK_WEBCLIENT_H #include #include #include using namespace ::testing; namespace { const std::string FAKE_SERVER = "http://fake-server/"; const std::string FAKE_PATH = "fake/api/path"; const std::string FAKE_QUERY = "FAKE_QUERY"; const std::string FAKE_PACKAGENAME = "com.example.fakepackage"; template struct LifetimeHelper { LifetimeHelper() : instance() { } template LifetimeHelper(Args&&... args) : instance(std::forward(args...)) { } QSharedPointer asSharedPtr() { return QSharedPointer(&instance, [](Interface*){}); } Mock instance; }; QSharedPointer responseForReply(const QSharedPointer& reply) { auto response = QSharedPointer(new click::web::Response(QSharedPointer(new QBuffer()))); response->setReply(reply); return response; } class MockClient : public click::web::Client { public: MockClient(const QSharedPointer& networkAccessManager, const QSharedPointer& sso) : Client(networkAccessManager, sso) { } // Mocking default arguments: https://groups.google.com/forum/#!topic/googlemock/XrabW20vV7o MOCK_METHOD6(callImpl, QSharedPointer( const std::string& iri, const std::string& method, bool sign, const std::map& headers, const std::string& data, const click::web::CallParams& params)); QSharedPointer call( const std::string& iri, const click::web::CallParams& params=click::web::CallParams()) { return callImpl(iri, "GET", false, std::map(), "", params); } QSharedPointer call( const std::string& iri, const std::string& method, bool sign = false, const std::map& headers = std::map(), const std::string& data = "", const click::web::CallParams& params=click::web::CallParams()) { return callImpl(iri, method, sign, headers, data, params); } }; } #endif // MOCK_WEBCLIENT_H unity-scope-click-0.1+14.04.20140417/scope/tests/integration/0000755000015301777760000000000012323764741024123 5ustar pbusernogroup00000000000000unity-scope-click-0.1+14.04.20140417/scope/tests/integration/webclient_integration.cpp0000644000015301777760000001042012323764232031176 0ustar pbusernogroup00000000000000/* * Copyright (C) 2014 Canonical Ltd. * * This program is free software: you can redistribute it and/or modify it * under the terms of the GNU General Public License version 3, as published * by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranties of * MERCHANTABILITY, SATISFACTORY QUALITY, or FITNESS FOR A PARTICULAR * PURPOSE. See the GNU General Public License for more details. * * You should have received a copy of the GNU General Public License along * with this program. If not, see . * * In addition, as a special exception, the copyright holders give * permission to link the code of portions of this program with the * OpenSSL library under certain conditions as described in each * individual source file, and distribute linked combinations * including the two. * You must obey the GNU General Public License in all respects * for all of the code used other than OpenSSL. If you modify * file(s) with this exception, you may extend this exception to your * version of the file(s), but you are not obligated to do so. If you * do not wish to do so, delete this exception statement from your * version. If you delete this exception statement from all source * files in the program, then also delete it here. */ #include #include #include #include #include #include #include #include #include namespace { struct IntegrationTest : public ::testing::Test { IntegrationTest() : app(argc, argv) { QObject::connect( &testTimeout, &QTimer::timeout, [this]() { Quit(); FAIL() << "Operation timed out."; }); } void SetUp() { const int tenSeconds = 10 * 1000; testTimeout.start(tenSeconds); } void TearDown() { testTimeout.stop(); } void Quit() { app.quit(); } int argc = 0; char** argv = nullptr; QCoreApplication app; QTimer testTimeout; }; } TEST_F(IntegrationTest, queryForArmhfPackagesReturnsCorrectResults) { click::web::Client ws(QSharedPointer( new click::network::AccessManager()), QSharedPointer( new click::CredentialsService())); click::web::CallParams params; params.add("q", "qr,architecture:armhf"); auto wr = ws.call(click::SEARCH_BASE_URL + click::SEARCH_PATH, params); QString content; QObject::connect( wr.data(), &click::web::Response::finished, [&, this](QString found_content) { content = found_content; Quit(); }); app.exec(); EXPECT_TRUE(content.size() > 0); } TEST_F(IntegrationTest, queryForArmhfPackagesCanBeParsed) { QSharedPointer namPtr( new click::network::AccessManager()); QSharedPointer ssoPtr( new click::CredentialsService()); QSharedPointer clientPtr( new click::web::Client(namPtr, ssoPtr)); click::Index index(clientPtr); click::PackageList packages; index.search("qr,architecture:armhf", [&, this](click::PackageList found_packages){ packages = found_packages; Quit(); }); app.exec(); EXPECT_TRUE(packages.size() > 0); } TEST_F(IntegrationTest, detailsCanBeParsed) { const std::string sample_name("com.ubuntu.developer.alecu.qr-code"); QSharedPointer namPtr( new click::network::AccessManager()); QSharedPointer ssoPtr( new click::CredentialsService()); QSharedPointer clientPtr( new click::web::Client(namPtr, ssoPtr)); click::Index index(clientPtr); index.get_details(sample_name, [&](click::PackageDetails details, click::Index::Error){ EXPECT_EQ(details.package.name, sample_name); Quit(); }); app.exec(); } unity-scope-click-0.1+14.04.20140417/scope/tests/integration/CMakeLists.txt0000644000015301777760000000123312323764232026655 0ustar pbusernogroup00000000000000SET (INTEGRATION_TARGET click_scope_integration_tests) # Qt5 bits SET (CMAKE_INCLUDE_CURRENT_DIR ON) SET (CMAKE_AUTOMOC ON) find_package(Qt5Core REQUIRED) include_directories (${CMAKE_SOURCE_DIR}/scope) FILE (GLOB TEST_SOURCES *.cpp) FILE (GLOB TEST_HEADERS *.h) add_executable (${INTEGRATION_TARGET} webclient_integration.cpp ) qt5_use_modules(${INTEGRATION_TARGET} Core DBus Network Test) target_link_libraries (${INTEGRATION_TARGET} ${SCOPE_LIB_UNVERSIONED} gmock gmock_main ${CMAKE_THREAD_LIBS_INIT} ) add_custom_target (test-integration-click-scope COMMAND ${CMAKE_CURRENT_BINARY_DIR}/${INTEGRATION_TARGET} DEPENDS ${INTEGRATION_TARGET} ) unity-scope-click-0.1+14.04.20140417/scope/tests/test_download_manager.cpp0000644000015301777760000004523712323764232026652 0ustar pbusernogroup00000000000000/* * Copyright (C) 2014 Canonical Ltd. * * This program is free software: you can redistribute it and/or modify it * under the terms of the GNU General Public License version 3, as published * by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranties of * MERCHANTABILITY, SATISFACTORY QUALITY, or FITNESS FOR A PARTICULAR * PURPOSE. See the GNU General Public License for more details. * * You should have received a copy of the GNU General Public License along * with this program. If not, see . * * In addition, as a special exception, the copyright holders give * permission to link the code of portions of this program with the * OpenSSL library under certain conditions as described in each * individual source file, and distribute linked combinations * including the two. * You must obey the GNU General Public License in all respects * for all of the code used other than OpenSSL. If you modify * file(s) with this exception, you may extend this exception to your * version of the file(s), but you are not obligated to do so. If you * do not wish to do so, delete this exception statement from your * version. If you delete this exception statement from all source * files in the program, then also delete it here. */ #include #include #include #include #include #include #include #include #include #include #include #include "mock_network_access_manager.h" #include "mock_ubuntuone_credentials.h" #include "mock_ubuntu_download_manager.h" namespace udm = Ubuntu::DownloadManager; #include void PrintTo(const QString& str, ::std::ostream* os) { *os << "QString(\"" << str.toStdString() << "\")"; } namespace { const QString TEST_URL("http://test.local/"); const QString TEST_HEADER_VALUE("test header value"); const QString TEST_APP_ID("test_app_id"); const QString TEST_CLICK_TOKEN_VALUE("test token value"); const QString TEST_DOWNLOAD_ID("/com/ubuntu/download_manager/test"); const QString TEST_DOWNLOADERROR_STRING("test downloadError string"); struct CredsNetworkTestParameters { public: CredsNetworkTestParameters(bool credsFound = true, bool replySignalsError = false, int replyStatusCode = 200, bool replyHasClickRawHeader = true, bool expectSuccessSignal = true) : credsFound(credsFound), replySignalsError(replySignalsError), replyStatusCode(replyStatusCode), replyHasClickRawHeader(replyHasClickRawHeader), expectSuccessSignal(expectSuccessSignal) {}; bool credsFound; bool replySignalsError; int replyStatusCode; bool replyHasClickRawHeader; bool expectSuccessSignal; }; ::std::ostream& operator<<(::std::ostream& os, const CredsNetworkTestParameters& p) { return os << "creds[" << (p.credsFound ? "x" : " ") << "] " << "replySignalsError[" << (p.replySignalsError ? "x" : " ") << "] " << "statusCode[" << p.replyStatusCode << "] " << "replyHasClickRawHeader[" << (p.replyHasClickRawHeader ? "x" : " ") << "] " << "expectSuccessSignal[" << (p.expectSuccessSignal ? "x" : " ") << "] "; } struct StartDownloadTestParameters { public: StartDownloadTestParameters(bool clickTokenFetchSignalsError = false, bool downloadSignalsError = false, bool expectSuccessSignal = true) : clickTokenFetchSignalsError(clickTokenFetchSignalsError), downloadSignalsError(downloadSignalsError), expectSuccessSignal(expectSuccessSignal) {}; bool clickTokenFetchSignalsError; bool downloadSignalsError; bool expectSuccessSignal; }; ::std::ostream& operator<<(::std::ostream& os, const StartDownloadTestParameters& p) { return os << "clickTokenFetchSignalsError[" << (p.clickTokenFetchSignalsError ? "x" : " ") << "] " << "downloadSignalsError[" << (p.downloadSignalsError ? "x" : " ") << "] " << "expectSuccessSignal[" << (p.expectSuccessSignal ? "x" : " ") << "] "; } struct DownloadManagerTestBase { DownloadManagerTestBase() : app(argc, argv), mockNam(new MockNetworkAccessManager()), mockCredentialsService(new MockCredentialsService()), mockReplyPtr(&mockReply, [](click::network::Reply*) {}), mockSystemDownloadManager(new MockSystemDownloadManager()) { signalTimer.setSingleShot(true); testTimeout.setSingleShot(true); QObject::connect( &testTimeout, &QTimer::timeout, [this]() { app.quit(); FAIL() << "Operation timed out."; } ); } void SetUp() { const int oneSecondInMsec = 1000; testTimeout.start(10 * oneSecondInMsec); } void Quit() { app.quit(); } int argc = 0; char** argv = nullptr; QCoreApplication app; QTimer testTimeout; QTimer signalTimer; QSharedPointer mockNam; QSharedPointer mockCredentialsService; ::testing::NiceMock mockReply; QSharedPointer mockReplyPtr; QSharedPointer mockSystemDownloadManager; }; struct DISABLED_DownloadManagerStartDownloadTest : public DownloadManagerTestBase, public ::testing::TestWithParam { public: }; struct DISABLED_DownloadManagerCredsNetworkTest : public DownloadManagerTestBase, public ::testing::TestWithParam { public: void signalEmptyTokenFromMockCredsService() { UbuntuOne::Token token; mockCredentialsService->credentialsFound(token); } void signalErrorAfterDelay() { // delay emitting this signal so that the download manager has // time to connect to the signal first, as the (mock)Reply is // returned by the (mock)Nam. QObject::connect(&signalTimer, &QTimer::timeout, [this]() { mockReplyPtr->error(QNetworkReply::UnknownNetworkError); }); signalTimer.start(10); } void signalFinishedAfterDelay() { QObject::connect(&signalTimer, &QTimer::timeout, [this]() { mockReplyPtr->finished(); }); signalTimer.start(10); } }; struct DownloadManagerMockClient { MOCK_METHOD0(onCredentialsNotFoundEmitted, void()); MOCK_METHOD1(onClickTokenFetchedEmitted, void(QString clickToken)); MOCK_METHOD1(onClickTokenFetchErrorEmitted, void(QString errorMessage)); MOCK_METHOD1(onDownloadStartedEmitted, void(QString id)); MOCK_METHOD1(onDownloadErrorEmitted, void(QString errorMessage)); }; } // anon namespace TEST_P(DISABLED_DownloadManagerCredsNetworkTest, TestFetchClickToken) { using namespace ::testing; CredsNetworkTestParameters p = GetParam(); QList > emptyHeaderPairs; ON_CALL(mockReply, rawHeaderPairs()).WillByDefault(Return(emptyHeaderPairs)); ON_CALL(mockReply, readAll()).WillByDefault(Return(QByteArray("bogus readAll() return"))); if (p.credsFound) { EXPECT_CALL(*mockCredentialsService, getCredentials()) .Times(1).WillOnce( InvokeWithoutArgs(this, &DISABLED_DownloadManagerCredsNetworkTest::signalEmptyTokenFromMockCredsService)); if (p.replySignalsError) { EXPECT_CALL(*mockNam, head(_)).WillOnce( DoAll( InvokeWithoutArgs(this, &DISABLED_DownloadManagerCredsNetworkTest::signalErrorAfterDelay), Return(mockReplyPtr))); EXPECT_CALL(mockReply, errorString()).Times(1).WillOnce(Return(QString("Bogus error for tests"))); } else { EXPECT_CALL(*mockNam, head(_)).WillOnce( DoAll( InvokeWithoutArgs(this, &DISABLED_DownloadManagerCredsNetworkTest::signalFinishedAfterDelay), Return(mockReplyPtr))); EXPECT_CALL(mockReply, attribute(QNetworkRequest::HttpStatusCodeAttribute)) .Times(1).WillOnce(Return(QVariant(p.replyStatusCode))); if (p.replyStatusCode == 200) { EXPECT_CALL(mockReply, hasRawHeader(click::CLICK_TOKEN_HEADER())) .Times(1).WillOnce(Return(p.replyHasClickRawHeader)); if (p.replyHasClickRawHeader) { EXPECT_CALL(mockReply, rawHeader(click::CLICK_TOKEN_HEADER())) .Times(1).WillOnce(Return(TEST_HEADER_VALUE)); } } } } else { EXPECT_CALL(*mockCredentialsService, getCredentials()) .Times(1).WillOnce(InvokeWithoutArgs(mockCredentialsService.data(), &MockCredentialsService::credentialsNotFound)); EXPECT_CALL(*mockNam, head(_)).Times(0); } click::DownloadManager dm(mockNam, mockCredentialsService, mockSystemDownloadManager); DownloadManagerMockClient mockDownloadManagerClient; QObject::connect(&dm, &click::DownloadManager::credentialsNotFound, [&mockDownloadManagerClient]() { mockDownloadManagerClient.onCredentialsNotFoundEmitted(); }); QObject::connect(&dm, &click::DownloadManager::clickTokenFetchError, [&mockDownloadManagerClient](const QString& error) { mockDownloadManagerClient.onClickTokenFetchErrorEmitted(error); }); QObject::connect(&dm, &click::DownloadManager::clickTokenFetched, [&mockDownloadManagerClient](const QString& token) { mockDownloadManagerClient.onClickTokenFetchedEmitted(token); }); if (p.expectSuccessSignal) { EXPECT_CALL(mockDownloadManagerClient, onClickTokenFetchedEmitted(TEST_HEADER_VALUE)) .Times(1) .WillOnce( InvokeWithoutArgs( this, &DISABLED_DownloadManagerCredsNetworkTest::Quit)); EXPECT_CALL(mockDownloadManagerClient, onClickTokenFetchErrorEmitted(_)).Times(0); } else { if (p.credsFound) { EXPECT_CALL(mockDownloadManagerClient, onClickTokenFetchErrorEmitted(_)) .Times(1) .WillOnce( InvokeWithoutArgs( this, &DISABLED_DownloadManagerCredsNetworkTest::Quit)); } else { EXPECT_CALL(mockDownloadManagerClient, onCredentialsNotFoundEmitted()) .Times(1) .WillOnce( InvokeWithoutArgs( this, &DISABLED_DownloadManagerCredsNetworkTest::Quit)); } EXPECT_CALL(mockDownloadManagerClient, onClickTokenFetchedEmitted(_)).Times(0); } // Now start the function we're testing, after a delay. This is // awkwardly verbose because QTimer::singleShot doesn't accept // arguments or lambdas. // We need to delay the call until after the app.exec() call so // that when we call app.quit() on success, there is a running app // to quit. QTimer timer; timer.setSingleShot(true); QObject::connect(&timer, &QTimer::timeout, [&dm]() { dm.fetchClickToken(TEST_URL); } ); timer.start(0); // now exec the app so events can proceed: app.exec(); } INSTANTIATE_TEST_CASE_P(DownloadManagerCredsNetworkTests, DISABLED_DownloadManagerCredsNetworkTest, ::testing::Values( // CredsNetworkTestParameters(credsFound, replySignalsError, replyStatusCode, replyHasClickRawHeader, expectSuccessSignal) CredsNetworkTestParameters(true, false, 200, true, true), // success CredsNetworkTestParameters(true, true, 200, true, false), // misc QNetworkReply error => error CredsNetworkTestParameters(true, false, 200, false, false), // no header => error CredsNetworkTestParameters(true, false, 401, true, false), // HTTP error status => error CredsNetworkTestParameters(false, false, 200, true, false) // no creds => error )); MATCHER(DownloadStructIsValid, "Download Struct does not match expected") { auto commandList = arg.getMetadata()["post-download-command"].toStringList(); return arg.getUrl() == TEST_URL && arg.getHash() == "" && arg.getAlgorithm() == "" && arg.getMetadata()["app_id"] == QVariant(TEST_APP_ID) && commandList[0] == "/bin/sh" && commandList[1] == "-c" && commandList[3] == "$file" && arg.getHeaders()["X-Click-Token"] == TEST_CLICK_TOKEN_VALUE; } TEST_P(DISABLED_DownloadManagerStartDownloadTest, TestStartDownload) { using namespace ::testing; StartDownloadTestParameters p = GetParam(); click::DownloadManager dm(mockNam, mockCredentialsService, mockSystemDownloadManager); // mockError is heap-allocated because downloadWithError will delete it. MockError mockError; // = new MockError(); NiceMock downloadWithError(&mockError); ON_CALL(downloadWithError, isError()).WillByDefault(Return(true)); ON_CALL(downloadWithError, error()).WillByDefault(Return(&mockError)); NiceMock successfulDownload; ON_CALL(successfulDownload, isError()).WillByDefault(Return(false)); // Just directly signal clickTokenFetched or error from // getCredentials(), no need to re-test the same code as the // previous test std::function clickTokenSignalFunc; if (p.clickTokenFetchSignalsError) { clickTokenSignalFunc = std::function([&](){ dm.clickTokenFetchError(TEST_DOWNLOADERROR_STRING); }); EXPECT_CALL(*mockSystemDownloadManager, createDownload(_)).Times(0); } else { clickTokenSignalFunc = std::function([&](){ dm.clickTokenFetched(TEST_CLICK_TOKEN_VALUE); }); std::function downloadCreatedSignalFunc; // NOTE: udm::Download doesn't have virtual functions, so mocking // them doesn't work and we have to construct objects that will // behave correctly without mock return values, using overridden constructors: if (p.downloadSignalsError) { EXPECT_CALL(mockError, errorString()).Times(1).WillOnce(Return(TEST_DOWNLOADERROR_STRING)); downloadCreatedSignalFunc = std::function([&](){ mockSystemDownloadManager->downloadCreated(&downloadWithError); }); } else { EXPECT_CALL(mockError, errorString()).Times(0); downloadCreatedSignalFunc = std::function([&](){ mockSystemDownloadManager->downloadCreated(&successfulDownload); }); } EXPECT_CALL(*mockSystemDownloadManager, createDownload(DownloadStructIsValid())).Times(1).WillOnce(InvokeWithoutArgs(downloadCreatedSignalFunc)); } EXPECT_CALL(*mockCredentialsService, getCredentials()) .Times(1).WillOnce(InvokeWithoutArgs(clickTokenSignalFunc)); DownloadManagerMockClient mockDownloadManagerClient; QObject::connect(&dm, &click::DownloadManager::downloadError, [&mockDownloadManagerClient](const QString& error) { mockDownloadManagerClient.onDownloadErrorEmitted(error); }); QObject::connect(&dm, &click::DownloadManager::downloadStarted, [&mockDownloadManagerClient](const QString& downloadId) { qDebug() << "in lambda connected to click::dm::downloadstarted"; mockDownloadManagerClient.onDownloadStartedEmitted(downloadId); }); if (p.expectSuccessSignal) { EXPECT_CALL(mockDownloadManagerClient, onDownloadStartedEmitted(TEST_DOWNLOAD_ID)) .Times(1) .WillOnce( InvokeWithoutArgs( this, &DISABLED_DownloadManagerStartDownloadTest::Quit)); EXPECT_CALL(mockDownloadManagerClient, onDownloadErrorEmitted(_)).Times(0); EXPECT_CALL(successfulDownload, id()).Times(1).WillOnce(Return(TEST_DOWNLOAD_ID)); EXPECT_CALL(successfulDownload, start()).Times(1); } else { EXPECT_CALL(mockDownloadManagerClient, onDownloadErrorEmitted(TEST_DOWNLOADERROR_STRING)) .Times(1) .WillOnce( InvokeWithoutArgs( this, &DISABLED_DownloadManagerStartDownloadTest::Quit)); EXPECT_CALL(mockDownloadManagerClient, onDownloadStartedEmitted(_)).Times(0); } QTimer timer; timer.setSingleShot(true); QObject::connect(&timer, &QTimer::timeout, [&dm]() { dm.startDownload(TEST_URL, TEST_APP_ID); } ); timer.start(0); // now exec the app so events can proceed: app.exec(); } INSTANTIATE_TEST_CASE_P(DownloadManagerStartDownloadTests, DISABLED_DownloadManagerStartDownloadTest, ::testing::Values( // params: (clickTokenFetchSignalsError, downloadSignalsError, expectSuccessSignal) StartDownloadTestParameters(false, false, true), StartDownloadTestParameters(true, false, false), StartDownloadTestParameters(false, true, false) )); class ArgZeroTest : public ::testing::TestWithParam {}; TEST_P(ArgZeroTest, testArgZero) { auto expected_arg = GetParam(); auto fake_command = "for n in " % click::QUOTED_ARG0 % "; do echo $n; done"; QStringList arguments; arguments << "-c" << fake_command << expected_arg; QProcess sh; sh.start("/bin/sh", arguments); EXPECT_TRUE(sh.waitForStarted()); sh.closeWriteChannel(); EXPECT_TRUE(sh.waitForFinished()); QByteArray result = sh.readAll().trimmed(); EXPECT_EQ(sh.exitCode(), 0); ASSERT_EQ(expected_arg, QString(result)); } INSTANTIATE_TEST_CASE_P(TestSampleFilenames, ArgZeroTest, ::testing::Values("sample_filename.click", "spaced_filename (1).click")); unity-scope-click-0.1+14.04.20140417/scope/tests/download_manager_tool/0000755000015301777760000000000012323764741026136 5ustar pbusernogroup00000000000000unity-scope-click-0.1+14.04.20140417/scope/tests/download_manager_tool/download_manager_tool.h0000644000015301777760000000370312323764232032643 0ustar pbusernogroup00000000000000/* * Copyright (C) 2014 Canonical Ltd. * * This program is free software: you can redistribute it and/or modify it * under the terms of the GNU General Public License version 3, as published * by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranties of * MERCHANTABILITY, SATISFACTORY QUALITY, or FITNESS FOR A PARTICULAR * PURPOSE. See the GNU General Public License for more details. * * You should have received a copy of the GNU General Public License along * with this program. If not, see . * * In addition, as a special exception, the copyright holders give * permission to link the code of portions of this program with the * OpenSSL library under certain conditions as described in each * individual source file, and distribute linked combinations * including the two. * You must obey the GNU General Public License in all respects * for all of the code used other than OpenSSL. If you modify * file(s) with this exception, you may extend this exception to your * version of the file(s), but you are not obligated to do so. If you * do not wish to do so, delete this exception statement from your * version. If you delete this exception statement from all source * files in the program, then also delete it here. */ #ifndef _DOWNLOAD_MANAGER_TOOL_H_ #define _DOWNLOAD_MANAGER_TOOL_H_ #include #include class DownloadManagerTool : public QObject { Q_OBJECT public: explicit DownloadManagerTool(QObject *parent=0); public slots: void fetchClickToken(QString url); void startDownload(QString url, QString appId); private slots: void handleResponse(QString response); signals: void finished(); private: click::DownloadManager *_dm; }; #endif /* _DOWNLOAD_MANAGER_TOOL_H_ */ unity-scope-click-0.1+14.04.20140417/scope/tests/download_manager_tool/download_manager_tool.cpp0000644000015301777760000001155412323764232033201 0ustar pbusernogroup00000000000000/* * Copyright (C) 2014 Canonical Ltd. * * This program is free software: you can redistribute it and/or modify it * under the terms of the GNU General Public License version 3, as published * by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranties of * MERCHANTABILITY, SATISFACTORY QUALITY, or FITNESS FOR A PARTICULAR * PURPOSE. See the GNU General Public License for more details. * * You should have received a copy of the GNU General Public License along * with this program. If not, see . * * In addition, as a special exception, the copyright holders give * permission to link the code of portions of this program with the * OpenSSL library under certain conditions as described in each * individual source file, and distribute linked combinations * including the two. * You must obey the GNU General Public License in all respects * for all of the code used other than OpenSSL. If you modify * file(s) with this exception, you may extend this exception to your * version of the file(s), but you are not obligated to do so. If you * do not wish to do so, delete this exception statement from your * version. If you delete this exception statement from all source * files in the program, then also delete it here. */ #include #include #include #include #include #include #include #include DownloadManagerTool::DownloadManagerTool(QObject *parent): QObject(parent) { _dm = new click::DownloadManager(QSharedPointer(new click::network::AccessManager()), QSharedPointer(new click::CredentialsService()), QSharedPointer( Ubuntu::DownloadManager::Manager::createSessionManager())); } void DownloadManagerTool::handleResponse(QString response) { QTextStream(stdout) << "DONE: response is " << response << "\n"; emit finished(); } void DownloadManagerTool::fetchClickToken(QString url) { QObject::connect(_dm, &click::DownloadManager::clickTokenFetched, this, &DownloadManagerTool::handleResponse); QObject::connect(_dm, &click::DownloadManager::clickTokenFetchError, this, &DownloadManagerTool::handleResponse); _dm->fetchClickToken(url); } void DownloadManagerTool::startDownload(QString url, QString appId) { QObject::connect(_dm, &click::DownloadManager::downloadStarted, this, &DownloadManagerTool::handleResponse); QObject::connect(_dm, &click::DownloadManager::downloadError, this, &DownloadManagerTool::handleResponse); _dm->startDownload(url, appId); } int main(int argc, char *argv[]) { QCoreApplication a(argc, argv); DownloadManagerTool tool; click::Downloader downloader(QSharedPointer(new click::network::AccessManager())); QTimer timer; timer.setSingleShot(true); QObject::connect(&tool, SIGNAL(finished()), &a, SLOT(quit())); if (argc == 2) { QObject::connect(&timer, &QTimer::timeout, [&]() { tool.fetchClickToken(QString(argv[1])); } ); } else if (argc == 3) { QObject::connect(&timer, &QTimer::timeout, [&]() { downloader.startDownload(std::string(argv[1]), std::string(argv[2]), [&a] (std::pair arg){ auto error = arg.second; if (error == click::InstallError::NoError) { std::cout << " Success, got download ID:" << arg.first << std::endl; } else { std::cout << " Error:" << arg.first << std::endl; } a.quit(); }); } ); } else { QTextStream(stderr) << "Usages:\n" << "download_manager_tool https://public.apps.ubuntu.com/download/<>\n" << "\t - when run with a valid U1 credential in the system, should print the click token to stdout.\n" << "download_manager_tool url app_id\n" << "\t - with a valid credential, should begin a download.\n"; return 1; } timer.start(0); qInstallMessageHandler(0); return a.exec(); } unity-scope-click-0.1+14.04.20140417/scope/tests/download_manager_tool/CMakeLists.txt0000644000015301777760000000046112323764232030672 0ustar pbusernogroup00000000000000set(DOWNLOAD_MANAGER_TOOL_TARGET download_manager_tool) include_directories ( ${CMAKE_SOURCE_DIR}/scope/click ) add_executable (${DOWNLOAD_MANAGER_TOOL_TARGET} download_manager_tool.cpp download_manager_tool.h ) target_link_libraries (${DOWNLOAD_MANAGER_TOOL_TARGET} ${SCOPE_LIB_UNVERSIONED} ) unity-scope-click-0.1+14.04.20140417/scope/tests/test_data.cpp.in0000644000015301777760000000333212323764232024655 0ustar pbusernogroup00000000000000/* * Copyright (C) 2014 Canonical Ltd. * * This program is free software: you can redistribute it and/or modify it * under the terms of the GNU General Public License version 3, as published * by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranties of * MERCHANTABILITY, SATISFACTORY QUALITY, or FITNESS FOR A PARTICULAR * PURPOSE. See the GNU General Public License for more details. * * You should have received a copy of the GNU General Public License along * with this program. If not, see . * * In addition, as a special exception, the copyright holders give * permission to link the code of portions of this program with the * OpenSSL library under certain conditions as described in each * individual source file, and distribute linked combinations * including the two. * You must obey the GNU General Public License in all respects * for all of the code used other than OpenSSL. If you modify * file(s) with this exception, you may extend this exception to your * version of the file(s), but you are not obligated to do so. If you * do not wish to do so, delete this exception statement from your * version. If you delete this exception statement from all source * files in the program, then also delete it here. */ #include "test_data.h" const std::string& testing::systemApplicationsDirectoryForTesting() { static const std::string s{"@CMAKE_CURRENT_SOURCE_DIR@/applications/system"}; return s; } const std::string& testing::userApplicationsDirectoryForTesting() { static const std::string s{"@CMAKE_CURRENT_SOURCE_DIR@/applications/user"}; return s; } unity-scope-click-0.1+14.04.20140417/scope/tests/test_index.cpp0000644000015301777760000003373212323764232024455 0ustar pbusernogroup00000000000000/* * Copyright (C) 2014 Canonical Ltd. * * This program is free software: you can redistribute it and/or modify it * under the terms of the GNU General Public License version 3, as published * by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranties of * MERCHANTABILITY, SATISFACTORY QUALITY, or FITNESS FOR A PARTICULAR * PURPOSE. See the GNU General Public License for more details. * * You should have received a copy of the GNU General Public License along * with this program. If not, see . * * In addition, as a special exception, the copyright holders give * permission to link the code of portions of this program with the * OpenSSL library under certain conditions as described in each * individual source file, and distribute linked combinations * including the two. * You must obey the GNU General Public License in all respects * for all of the code used other than OpenSSL. If you modify * file(s) with this exception, you may extend this exception to your * version of the file(s), but you are not obligated to do so. If you * do not wish to do so, delete this exception statement from your * version. If you delete this exception statement from all source * files in the program, then also delete it here. */ #include "click/index.h" #include "click/webclient.h" #include "mock_network_access_manager.h" #include "mock_ubuntuone_credentials.h" #include "mock_webclient.h" #include "fake_json.h" #include #include using namespace ::testing; namespace { class IndexTest : public ::testing::Test { protected: QSharedPointer clientPtr; QSharedPointer namPtr; QSharedPointer ssoPtr; std::shared_ptr indexPtr; virtual void SetUp() { namPtr.reset(new MockNetworkAccessManager()); ssoPtr.reset(new MockCredentialsService()); clientPtr.reset(new NiceMock(namPtr, ssoPtr)); indexPtr.reset(new click::Index(clientPtr)); } public: MOCK_METHOD1(search_callback, void(click::PackageList)); MOCK_METHOD2(details_callback, void(click::PackageDetails, click::Index::Error)); }; class MockPackageManager : public click::PackageManager, public ::testing::Test { public: MOCK_METHOD2(execute_uninstall_command, void(const std::string&, std::function)); }; } TEST_F(IndexTest, testSearchCallsWebservice) { LifetimeHelper reply; auto response = responseForReply(reply.asSharedPtr()); EXPECT_CALL(*clientPtr, callImpl(_, _, _, _, _, _)) .Times(1) .WillOnce(Return(response)); indexPtr->search("", [](click::PackageList) {}); } TEST_F(IndexTest, testSearchSendsQueryAsParam) { LifetimeHelper reply; auto response = responseForReply(reply.asSharedPtr()); click::web::CallParams params; params.add(click::QUERY_ARGNAME, FAKE_QUERY); EXPECT_CALL(*clientPtr, callImpl(_, _, _, _, _, params)) .Times(1) .WillOnce(Return(response)); indexPtr->search(FAKE_QUERY, [](click::PackageList) {}); } TEST_F(IndexTest, testSearchSendsRightPath) { LifetimeHelper reply; auto response = responseForReply(reply.asSharedPtr()); EXPECT_CALL(*clientPtr, callImpl(EndsWith(click::SEARCH_PATH), _, _, _, _, _)) .Times(1) .WillOnce(Return(response)); indexPtr->search("", [](click::PackageList) {}); } TEST_F(IndexTest, testSearchCallbackIsCalled) { LifetimeHelper reply; auto response = responseForReply(reply.asSharedPtr()); QByteArray fake_json("[]"); EXPECT_CALL(reply.instance, readAll()) .Times(1) .WillOnce(Return(fake_json)); EXPECT_CALL(*clientPtr, callImpl(_, _, _, _, _, _)) .Times(1) .WillOnce(Return(response)); EXPECT_CALL(*this, search_callback(_)).Times(1); indexPtr->search("", [this](click::PackageList packages){ search_callback(packages); }); response->replyFinished(); } TEST_F(IndexTest, testSearchEmptyJsonIsParsed) { LifetimeHelper reply; auto response = responseForReply(reply.asSharedPtr()); QByteArray fake_json("[]"); EXPECT_CALL(reply.instance, readAll()) .Times(1) .WillOnce(Return(fake_json)); EXPECT_CALL(*clientPtr, callImpl(_, _, _, _, _, _)) .Times(1) .WillOnce(Return(response)); click::PackageList empty_package_list; EXPECT_CALL(*this, search_callback(empty_package_list)).Times(1); indexPtr->search("", [this](click::PackageList packages){ search_callback(packages); }); response->replyFinished(); } TEST_F(IndexTest, testSearchSingleJsonIsParsed) { LifetimeHelper reply; auto response = responseForReply(reply.asSharedPtr()); QByteArray fake_json(FAKE_JSON_SEARCH_RESULT_ONE.c_str()); EXPECT_CALL(reply.instance, readAll()) .Times(1) .WillOnce(Return(fake_json)); EXPECT_CALL(*clientPtr, callImpl(_, _, _, _, _, _)) .Times(1) .WillOnce(Return(response)); click::PackageList single_package_list = { click::Package { "org.example.awesomelauncher", "Awesome Launcher", 1.99, "http://software-center.ubuntu.com/site_media/appmedia/2012/09/SPAZ.png", "http://search.apps.ubuntu.com/api/v1/package/org.example.awesomelauncher" } }; EXPECT_CALL(*this, search_callback(single_package_list)).Times(1); indexPtr->search("", [this](click::PackageList packages){ search_callback(packages); }); response->replyFinished(); } TEST_F(IndexTest, testSearchIsCancellable) { LifetimeHelper reply; auto response = responseForReply(reply.asSharedPtr()); EXPECT_CALL(*clientPtr, callImpl(_, _, _, _, _, _)) .Times(1) .WillOnce(Return(response)); auto search_operation = indexPtr->search("", [](click::PackageList) {}); EXPECT_CALL(reply.instance, abort()).Times(1); search_operation.cancel(); } TEST_F(IndexTest, DISABLED_testInvalidJsonIsIgnored) { // TODO, in upcoming branch } TEST_F(IndexTest, testSearchNetworkErrorIgnored) { LifetimeHelper reply; auto response = responseForReply(reply.asSharedPtr()); EXPECT_CALL(*clientPtr, callImpl(_, _, _, _, _, _)) .Times(1) .WillOnce(Return(response)); EXPECT_CALL(reply.instance, errorString()).Times(1).WillOnce(Return("fake error")); indexPtr->search("", [this](click::PackageList packages){ search_callback(packages); }); click::PackageList empty_package_list; EXPECT_CALL(*this, search_callback(empty_package_list)).Times(1); emit reply.instance.error(QNetworkReply::UnknownNetworkError); } TEST_F(IndexTest, testGetDetailsCallsWebservice) { LifetimeHelper reply; auto response = responseForReply(reply.asSharedPtr()); EXPECT_CALL(*clientPtr, callImpl(_, _, _, _, _, _)) .Times(1) .WillOnce(Return(response)); indexPtr->get_details("", [](click::PackageDetails, click::Index::Error) {}); } TEST_F(IndexTest, testGetDetailsSendsPackagename) { LifetimeHelper reply; auto response = responseForReply(reply.asSharedPtr()); EXPECT_CALL(*clientPtr, callImpl(EndsWith(FAKE_PACKAGENAME), _, _, _, _, _)) .Times(1) .WillOnce(Return(response)); indexPtr->get_details(FAKE_PACKAGENAME, [](click::PackageDetails, click::Index::Error) {}); } TEST_F(IndexTest, testGetDetailsSendsRightPath) { LifetimeHelper reply; auto response = responseForReply(reply.asSharedPtr()); EXPECT_CALL(*clientPtr, callImpl(StartsWith(click::SEARCH_BASE_URL + click::DETAILS_PATH), _, _, _, _, _)) .Times(1) .WillOnce(Return(response)); indexPtr->get_details(FAKE_PACKAGENAME, [](click::PackageDetails, click::Index::Error) {}); } TEST_F(IndexTest, testGetDetailsCallbackIsCalled) { LifetimeHelper reply; auto response = responseForReply(reply.asSharedPtr()); QByteArray fake_json(FAKE_JSON_PACKAGE_DETAILS.c_str()); EXPECT_CALL(reply.instance, readAll()) .Times(1) .WillOnce(Return(fake_json)); EXPECT_CALL(*clientPtr, callImpl(_, _, _, _, _, _)) .Times(1) .WillOnce(Return(response)); indexPtr->get_details("", [this](click::PackageDetails details, click::Index::Error error){ details_callback(details, error); }); EXPECT_CALL(*this, details_callback(_, _)).Times(1); response->replyFinished(); } TEST_F(IndexTest, testGetDetailsJsonIsParsed) { LifetimeHelper reply; auto response = responseForReply(reply.asSharedPtr()); QByteArray fake_json(FAKE_JSON_PACKAGE_DETAILS.c_str()); EXPECT_CALL(reply.instance, readAll()) .Times(1) .WillOnce(Return(fake_json)); EXPECT_CALL(*clientPtr, callImpl(_, _, _, _, _, _)) .Times(1) .WillOnce(Return(response)); indexPtr->get_details("", [this](click::PackageDetails details, click::Index::Error error){ details_callback(details, error); }); click::PackageDetails fake_details { { "ar.com.beuno.wheather-touch", "\u1F4A9 Weather", 1.99, "http://developer.staging.ubuntu.com/site_media/appmedia/2013/07/weather-icone-6797-64.png", "https://public.apps.staging.ubuntu.com/download/ar.com.beuno/wheather-touch/ar.com.beuno.wheather-touch-0.2", "0.2", }, "\u1F4A9 Weather\nA weather application.", "https://public.apps.staging.ubuntu.com/download/ar.com.beuno/wheather-touch/ar.com.beuno.wheather-touch-0.2", 3.5, "these, are, key, words", "tos", "Proprietary", "Beuno", "sshot0", {"sshot1", "sshot2"}, 177582, "0.2", "None" }; EXPECT_CALL(*this, details_callback(fake_details, _)).Times(1); response->replyFinished(); } TEST_F(IndexTest, testGetDetailsJsonUtf8) { LifetimeHelper reply; auto response = responseForReply(reply.asSharedPtr()); QByteArray appname_utf8("\xe5\xb0\x8f\xe6\xb5\xb7"); QByteArray appname_json("\\u5c0f\\u6d77"); qDebug() << "testGetDetailsJsonUtf8, title:" << appname_utf8.toPercentEncoding(" "); QByteArray fake_json = QByteArray(FAKE_JSON_PACKAGE_DETAILS.c_str()).replace("Weather", appname_json); EXPECT_CALL(reply.instance, readAll()) .Times(1) .WillOnce(Return(fake_json)); EXPECT_CALL(*clientPtr, callImpl(_, _, _, _, _, _)) .Times(1) .WillOnce(Return(response)); indexPtr->get_details("", [this, appname_utf8](click::PackageDetails details, click::Index::Error error){ details_callback(details, error); }); click::PackageDetails fake_details { { "ar.com.beuno.wheather-touch", std::string("\u1F4A9 ") + appname_utf8.constData(), 1.99, "http://developer.staging.ubuntu.com/site_media/appmedia/2013/07/weather-icone-6797-64.png", "https://public.apps.staging.ubuntu.com/download/ar.com.beuno/wheather-touch/ar.com.beuno.wheather-touch-0.2", "v0.1", }, (std::string("\u1F4A9 ") + std::string(appname_utf8.constData()) + "\nA weather application.").c_str(), "https://public.apps.staging.ubuntu.com/download/ar.com.beuno/wheather-touch/ar.com.beuno.wheather-touch-0.2", 3.5, "these, are, key, words", "tos", "Proprietary", "Beuno", "sshot0", {"sshot1", "sshot2"}, 177582, "0.2", "None" }; EXPECT_CALL(*this, details_callback(fake_details, _)).Times(1); response->replyFinished(); } TEST_F(IndexTest, testGetDetailsNetworkErrorReported) { LifetimeHelper reply; auto response = responseForReply(reply.asSharedPtr()); EXPECT_CALL(*clientPtr, callImpl(_, _, _, _, _, _)) .Times(1) .WillOnce(Return(response)); EXPECT_CALL(reply.instance, errorString()).Times(1).WillOnce(Return("fake error")); indexPtr->get_details("", [this](click::PackageDetails details, click::Index::Error error){ details_callback(details, error); }); EXPECT_CALL(*this, details_callback(_, click::Index::Error::NetworkError)).Times(1); emit reply.instance.error(QNetworkReply::UnknownNetworkError); } TEST_F(IndexTest, testGetDetailsIsCancellable) { LifetimeHelper reply; auto response = responseForReply(reply.asSharedPtr()); EXPECT_CALL(*clientPtr, callImpl(_, _, _, _, _, _)) .Times(1) .WillOnce(Return(response)); auto get_details_operation = indexPtr->get_details("", [](click::PackageDetails, click::Index::Error) {}); EXPECT_CALL(reply.instance, abort()).Times(1); get_details_operation.cancel(); } TEST_F(MockPackageManager, testUninstallCommandCorrect) { click::Package package = { "org.example.testapp", "Test App", 0.00, "/tmp/foo.png", "", "0.1.5" }; std::string expected = "pkcon -p remove org.example.testapp;0.1.5;all;local:click"; EXPECT_CALL(*this, execute_uninstall_command(expected, _)).Times(1); uninstall(package, [](int, std::string) {}); } unity-scope-click-0.1+14.04.20140417/scope/tests/fake_json.h0000644000015301777760000001202312323764232023701 0ustar pbusernogroup00000000000000/* * Copyright (C) 2014 Canonical Ltd. * * This program is free software: you can redistribute it and/or modify it * under the terms of the GNU General Public License version 3, as published * by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranties of * MERCHANTABILITY, SATISFACTORY QUALITY, or FITNESS FOR A PARTICULAR * PURPOSE. See the GNU General Public License for more details. * * You should have received a copy of the GNU General Public License along * with this program. If not, see . * * In addition, as a special exception, the copyright holders give * permission to link the code of portions of this program with the * OpenSSL library under certain conditions as described in each * individual source file, and distribute linked combinations * including the two. * You must obey the GNU General Public License in all respects * for all of the code used other than OpenSSL. If you modify * file(s) with this exception, you may extend this exception to your * version of the file(s), but you are not obligated to do so. If you * do not wish to do so, delete this exception statement from your * version. If you delete this exception statement from all source * files in the program, then also delete it here. */ #ifndef FAKE_JSON_H #define FAKE_JSON_H const std::string FAKE_JSON_REVIEWS_RESULT_ONE = R"foo( [ { "date_created": "2014-01-28T09:09:47.218Z", "date_deleted": null, "hide": false, "id": 1, "language": "en", "package_name": "com.example.fakepackage", "rating": 4, "review_text": "It is ok.", "reviewer_displayname": "Reviewer", "reviewer_username": "reviewer", "summary": "Review Summary", "usefulness_favorable": 0, "usefulness_total": 0, "version": "0.2" } ] )foo"; const std::string FAKE_JSON_SEARCH_RESULT_ONE = R"foo( [ { "name": "org.example.awesomelauncher", "title": "Awesome Launcher", "description": "This is an awesome launcher.", "price": 1.99, "icon_url": "http://software-center.ubuntu.com/site_media/appmedia/2012/09/SPAZ.png", "resource_url": "http://search.apps.ubuntu.com/api/v1/package/org.example.awesomelauncher" } ] )foo"; const std::string FAKE_JSON_SEARCH_RESULT_MANY = R"foo( [ { "name": "org.example.awesomelauncher", "title": "Awesome Launcher", "description": "This is an awesome launcher.", "price": 1.99, "icon_url": "http://software-center.ubuntu.com/site_media/appmedia/2012/09/SPAZ.png", "resource_url": "http://search.apps.ubuntu.com/api/v1/package/org.example.awesomelauncher" }, { "name": "org.example.fantastiqueapp", "title": "Fantastic App", "description": "This is a fantasticc app.", "price": 0.0, "icon_url": "http://assets.ubuntu.com/sites/ubuntu/504/u/img/ubuntu/features/icon-find-more-apps-64x64.png", "resource_url": "http://search.apps.ubuntu.com/api/v1/package/org.example.fantasticapp" }, { "name": "org.example.awesomewidget", "title": "Awesome Widget", "description": "This is an awesome widget.", "price": 1.99, "icon_url": "http://assets.ubuntu.com/sites/ubuntu/504/u/img/ubuntu/features/icon-photos-and-videos-64x64.png", "resource_url": "http://search.apps.ubuntu.com/api/v1/package/org.example.awesomewidget" } ] )foo"; const std::string FAKE_JSON_PACKAGE_DETAILS = R"foo( { "name": "ar.com.beuno.wheather-touch", "icon_url": "http://developer.staging.ubuntu.com/site_media/appmedia/2013/07/weather-icone-6797-64.png", "title": "\u1F4A9 Weather", "description": "\u1F4A9 Weather\nA weather application.", "download_url": "https://public.apps.staging.ubuntu.com/download/ar.com.beuno/wheather-touch/ar.com.beuno.wheather-touch-0.2", "rating": 3.5, "keywords": "these, are, key, words", "terms_of_service": "tos", "license": "Proprietary", "publisher": "Beuno", "screenshot_url": "sshot0", "screenshot_urls": ["sshot1", "sshot2"], "binary_filesize": 177582, "version": "0.2", "framework": "None", "website": "", "support_url": "http://beuno.com.ar", "price": 1.99, "license_key_path": "", "click_version": "0.1", "company_name": "", "icon_urls": { "64": "http://developer.staging.ubuntu.com/site_media/appmedia/2013/07/weather-icone-6797-64.png" }, "requires_license_key": false, "date_published": "2013-07-16T21:50:34.874000" } )foo"; #endif // FAKE_JSON_H unity-scope-click-0.1+14.04.20140417/scope/tests/test_reviews.cpp0000644000015301777760000002067112323764232025030 0ustar pbusernogroup00000000000000/* * Copyright (C) 2014 Canonical Ltd. * * This program is free software: you can redistribute it and/or modify it * under the terms of the GNU General Public License version 3, as published * by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranties of * MERCHANTABILITY, SATISFACTORY QUALITY, or FITNESS FOR A PARTICULAR * PURPOSE. See the GNU General Public License for more details. * * You should have received a copy of the GNU General Public License along * with this program. If not, see . * * In addition, as a special exception, the copyright holders give * permission to link the code of portions of this program with the * OpenSSL library under certain conditions as described in each * individual source file, and distribute linked combinations * including the two. * You must obey the GNU General Public License in all respects * for all of the code used other than OpenSSL. If you modify * file(s) with this exception, you may extend this exception to your * version of the file(s), but you are not obligated to do so. If you * do not wish to do so, delete this exception statement from your * version. If you delete this exception statement from all source * files in the program, then also delete it here. */ #include #include #include "fake_json.h" #include "mock_network_access_manager.h" #include "mock_ubuntuone_credentials.h" #include "mock_webclient.h" #include #include #include using namespace ::testing; namespace { class ReviewsTest : public ::testing::Test { protected: QSharedPointer clientPtr; QSharedPointer namPtr; QSharedPointer ssoPtr; std::shared_ptr reviewsPtr; virtual void SetUp() { namPtr.reset(new MockNetworkAccessManager()); ssoPtr.reset(new MockCredentialsService()); clientPtr.reset(new NiceMock(namPtr, ssoPtr)); reviewsPtr.reset(new click::Reviews(clientPtr)); } public: MOCK_METHOD2(reviews_callback, void(click::ReviewList, click::Reviews::Error)); }; } TEST_F(ReviewsTest, testFetchReviewsCallsWebservice) { LifetimeHelper reply; auto response = responseForReply(reply.asSharedPtr()); EXPECT_CALL(*clientPtr, callImpl(_, _, _, _, _, _)) .Times(1) .WillOnce(Return(response)); reviewsPtr->fetch_reviews("", [](click::ReviewList, click::Reviews::Error) {}); } TEST_F(ReviewsTest, testFetchReviewsSendsQueryAsParam) { LifetimeHelper reply; auto response = responseForReply(reply.asSharedPtr()); click::web::CallParams params; params.add(click::REVIEWS_QUERY_ARGNAME, FAKE_PACKAGENAME); EXPECT_CALL(*clientPtr, callImpl(_, _, _, _, _, params)) .Times(1) .WillOnce(Return(response)); reviewsPtr->fetch_reviews(FAKE_PACKAGENAME, [](click::ReviewList, click::Reviews::Error) {}); } TEST_F(ReviewsTest, testFetchReviewsSendsCorrectPath) { LifetimeHelper reply; auto response = responseForReply(reply.asSharedPtr()); EXPECT_CALL(*clientPtr, callImpl(EndsWith(click::REVIEWS_API_PATH), _, _, _, _, _)) .Times(1) .WillOnce(Return(response)); reviewsPtr->fetch_reviews(FAKE_PACKAGENAME, [](click::ReviewList, click::Reviews::Error) {}); } TEST_F(ReviewsTest, testFetchReviewsCallbackCalled) { LifetimeHelper reply; auto response = responseForReply(reply.asSharedPtr()); QByteArray fake_json("[]"); EXPECT_CALL(reply.instance, readAll()) .Times(1) .WillOnce(Return(fake_json)); EXPECT_CALL(*clientPtr, callImpl(_, _, _, _, _, _)) .Times(1) .WillOnce(Return(response)); EXPECT_CALL(*this, reviews_callback(_, _)).Times(1); reviewsPtr->fetch_reviews("", [this](click::ReviewList reviews, click::Reviews::Error error){ reviews_callback(reviews, error); }); response->replyFinished(); } TEST_F(ReviewsTest, testFetchReviewsEmptyJsonIsParsed) { LifetimeHelper reply; auto response = responseForReply(reply.asSharedPtr()); QByteArray fake_json("[]"); EXPECT_CALL(reply.instance, readAll()) .Times(1) .WillOnce(Return(fake_json)); EXPECT_CALL(*clientPtr, callImpl(_, _, _, _, _, _)) .Times(1) .WillOnce(Return(response)); click::ReviewList empty_reviews_list; EXPECT_CALL(*this, reviews_callback(empty_reviews_list, _)).Times(1); reviewsPtr->fetch_reviews("", [this](click::ReviewList reviews, click::Reviews::Error error){ reviews_callback(reviews, error); }); response->replyFinished(); } TEST_F(ReviewsTest, testFetchReviewsSingleJsonIsParsed) { LifetimeHelper reply; auto response = responseForReply(reply.asSharedPtr()); QByteArray fake_json(FAKE_JSON_REVIEWS_RESULT_ONE.c_str()); EXPECT_CALL(reply.instance, readAll()) .Times(1) .WillOnce(Return(fake_json)); EXPECT_CALL(*clientPtr, callImpl(_, _, _, _, _, _)) .Times(1) .WillOnce(Return(response)); click::ReviewList single_review_list = { click::Review { 1, 4, 0, 0, false, "2014-01-28T09:09:47.218Z", "null", FAKE_PACKAGENAME, "0.2", "en", "Review Summary", "It is ok.", "Reviewer", "reviewer" } }; EXPECT_CALL(*this, reviews_callback(single_review_list, _)).Times(1); reviewsPtr->fetch_reviews("", [this](click::ReviewList reviews, click::Reviews::Error error){ reviews_callback(reviews, error); }); response->replyFinished(); } TEST_F(ReviewsTest, testFetchReviewsNetworkErrorReported) { LifetimeHelper reply; auto response = responseForReply(reply.asSharedPtr()); EXPECT_CALL(*clientPtr, callImpl(_, _, _, _, _, _)) .Times(1) .WillOnce(Return(response)); EXPECT_CALL(reply.instance, errorString()) .Times(1) .WillOnce(Return("fake error")); reviewsPtr->fetch_reviews("", [this](click::ReviewList reviews, click::Reviews::Error error){ reviews_callback(reviews, error); }); click::ReviewList empty_reviews_list; EXPECT_CALL(*this, reviews_callback(empty_reviews_list, click::Reviews::Error::NetworkError)) .Times(1); emit reply.instance.error(QNetworkReply::UnknownNetworkError); } TEST_F(ReviewsTest, testFetchReviewsIsCancellable) { LifetimeHelper reply; auto response = responseForReply(reply.asSharedPtr()); EXPECT_CALL(*clientPtr, callImpl(_, _, _, _, _, _)) .Times(1) .WillOnce(Return(response)); auto fetch_reviews_op = reviewsPtr->fetch_reviews("", [](click::ReviewList, click::Reviews::Error) {}); EXPECT_CALL(reply.instance, abort()).Times(1); fetch_reviews_op.cancel(); } TEST_F(ReviewsTest, testGetBaseUrl) { const char *value = getenv(click::REVIEWS_BASE_URL_ENVVAR.c_str()); if (value != NULL) { ASSERT_TRUE(unsetenv(click::REVIEWS_BASE_URL_ENVVAR.c_str()) == 0); } ASSERT_TRUE(click::Reviews::get_base_url() == click::REVIEWS_BASE_URL); } TEST_F(ReviewsTest, testGetBaseUrlFromEnv) { ASSERT_TRUE(setenv(click::REVIEWS_BASE_URL_ENVVAR.c_str(), FAKE_SERVER.c_str(), 1) == 0); ASSERT_TRUE(click::Reviews::get_base_url() == FAKE_SERVER); ASSERT_TRUE(unsetenv(click::REVIEWS_BASE_URL_ENVVAR.c_str()) == 0); } unity-scope-click-0.1+14.04.20140417/scope/tests/applications/0000755000015301777760000000000012323764741024266 5ustar pbusernogroup00000000000000unity-scope-click-0.1+14.04.20140417/scope/tests/applications/user/0000755000015301777760000000000012323764741025244 5ustar pbusernogroup00000000000000././@LongLink0000000000000000000000000000015300000000000011214 Lustar 00000000000000unity-scope-click-0.1+14.04.20140417/scope/tests/applications/user/non-click-app-without-exception.desktopunity-scope-click-0.1+14.04.20140417/scope/tests/applications/user/non-click-app-without-exception.d0000644000015301777760000000041512323764232033534 0ustar pbusernogroup00000000000000[Desktop Entry] Type=Application Name=Sample Desktop-only non-click app GenericName=NonClickAppWithoutException Comment=multiline description goes here Exec=messaging-app %u Terminal=false Icon=sample-desktop-app MimeType=x-scheme-handler/contact;x-scheme-handler/call ././@LongLink0000000000000000000000000000015200000000000011213 Lustar 00000000000000unity-scope-click-0.1+14.04.20140417/scope/tests/applications/user/com.ubuntu.notes_notes_1.4.242.desktopunity-scope-click-0.1+14.04.20140417/scope/tests/applications/user/com.ubuntu.notes_notes_1.4.242.de0000644000015301777760000000074112323764232033101 0ustar pbusernogroup00000000000000[Desktop Entry] Type=Application Exec=aa-exec-click -p com.ubuntu.notes_notes_1.4.242 -- qmlscene $@ -I ./usr/lib/arm-linux-gnueabihf/qt5/qml NotesApp.qml Name=Notes GenericName=Notes application for Ubuntu Icon=notepad Terminal=false X-Ubuntu-Touch=true X-Ubuntu-StageHint=SideStage X-Ubuntu-Gettext-Domain=com.ubuntu.notes X-Ubuntu-Single-Instance=true Path=/usr/share/click/preinstalled/.click/users/@all/com.ubuntu.notes X-Ubuntu-Application-ID=com.ubuntu.notes_notes_1.4.242 ././@LongLink0000000000000000000000000000021600000000000011214 Lustar 00000000000000unity-scope-click-0.1+14.04.20140417/scope/tests/applications/user/com.ubuntu.developer.webapps.webapp-facebook_webapp-facebook_1.0.5.desktopunity-scope-click-0.1+14.04.20140417/scope/tests/applications/user/com.ubuntu.developer.webapps.weba0000644000015301777760000000125412323764232033624 0ustar pbusernogroup00000000000000[Desktop Entry] Type=Application Terminal=false Exec=aa-exec-click -p com.ubuntu.developer.webapps.webapp-facebook_webapp-facebook_1.0.5 -- webbrowser-app --enable-back-forward --webapp --webappUrlPatterns=https?://m.facebook.com/* https://m.facebook.com/ Name=Facebook Icon=/usr/share/click/preinstalled/.click/users/@all/com.ubuntu.developer.webapps.webapp-facebook/./facebook.png X-Ubuntu-Touch=true X-Ubuntu-StageHint=SideStage X-Ubuntu-Single-Instance=true Path=/usr/share/click/preinstalled/.click/users/@all/com.ubuntu.developer.webapps.webapp-facebook X-Ubuntu-Old-Icon=./facebook.png X-Ubuntu-Application-ID=com.ubuntu.developer.webapps.webapp-facebook_webapp-facebook_1.0.5 ././@LongLink0000000000000000000000000000021200000000000011210 Lustar 00000000000000unity-scope-click-0.1+14.04.20140417/scope/tests/applications/user/com.ubuntu.developer.webapps.webapp-amazon_webapp-amazon_1.0.6.desktopunity-scope-click-0.1+14.04.20140417/scope/tests/applications/user/com.ubuntu.developer.webapps.weba0000644000015301777760000000120412323764232033617 0ustar pbusernogroup00000000000000[Desktop Entry] Type=Application Terminal=false Exec=aa-exec-click -p com.ubuntu.developer.webapps.webapp-amazon_webapp-amazon_1.0.6 -- webbrowser-app --enable-back-forward --webapp --webappUrlPatterns=https?://www.amazon.com/*,https?://s.amazon-adsystem.com/* http://www.amazon.com/gp/aw Name=Amazon Icon=/usr/share/click/preinstalled/.click/users/@all/com.ubuntu.developer.webapps.webapp-amazon/./amazon.png X-Ubuntu-Touch=true Path=/usr/share/click/preinstalled/.click/users/@all/com.ubuntu.developer.webapps.webapp-amazon X-Ubuntu-Old-Icon=./amazon.png X-Ubuntu-Application-ID=com.ubuntu.developer.webapps.webapp-amazon_webapp-amazon_1.0.6 ././@LongLink0000000000000000000000000000022000000000000011207 Lustar 00000000000000unity-scope-click-0.1+14.04.20140417/scope/tests/applications/user/com.ubuntu.developer.webapps.webapp-ubuntuone_webapp-ubuntuone_1.0.4.desktopunity-scope-click-0.1+14.04.20140417/scope/tests/applications/user/com.ubuntu.developer.webapps.weba0000644000015301777760000000126512323764232033626 0ustar pbusernogroup00000000000000[Desktop Entry] Type=Application Terminal=false Exec=aa-exec-click -p com.ubuntu.developer.webapps.webapp-ubuntuone_webapp-ubuntuone_1.0.4 -- webbrowser-app --enable-back-forward --webappUrlPatterns=https?://one.ubuntu.com/*,https?://login.ubuntu.com/* --webapp https://one.ubuntu.com/ Name=Ubuntu One Icon=/usr/share/click/preinstalled/.click/users/@all/com.ubuntu.developer.webapps.webapp-ubuntuone/./ubuntuone.png X-Ubuntu-Touch=true X-Ubuntu-Single-Instance=true Path=/usr/share/click/preinstalled/.click/users/@all/com.ubuntu.developer.webapps.webapp-ubuntuone X-Ubuntu-Old-Icon=./ubuntuone.png X-Ubuntu-Application-ID=com.ubuntu.developer.webapps.webapp-ubuntuone_webapp-ubuntuone_1.0.4 ././@LongLink0000000000000000000000000000015400000000000011215 Lustar 00000000000000unity-scope-click-0.1+14.04.20140417/scope/tests/applications/user/com.ubuntu.sudoku_sudoku_1.0.142.desktopunity-scope-click-0.1+14.04.20140417/scope/tests/applications/user/com.ubuntu.sudoku_sudoku_1.0.142.0000644000015301777760000000103212323764232033121 0ustar pbusernogroup00000000000000[Desktop Entry] Name=Sudoku Version=1.0 Comment=Sudoku Game for Ubuntu Touch Exec=aa-exec-click -p com.ubuntu.sudoku_sudoku_1.0.142 -- qmlscene sudoku-app.qml Icon=/usr/share/click/preinstalled/.click/users/@all/com.ubuntu.sudoku/SudokuGameIcon.png Terminal=false Type=Application X-Ubuntu-Touch=true X-Ubuntu-StageHint=SideStage X-Ubuntu-Gettext-Domain=com.ubuntu.sudoku Path=/usr/share/click/preinstalled/.click/users/@all/com.ubuntu.sudoku X-Ubuntu-Old-Icon=SudokuGameIcon.png X-Ubuntu-Application-ID=com.ubuntu.sudoku_sudoku_1.0.142 ././@LongLink0000000000000000000000000000015200000000000011213 Lustar 00000000000000unity-scope-click-0.1+14.04.20140417/scope/tests/applications/user/com.ubuntu.music_music_1.1.329.desktopunity-scope-click-0.1+14.04.20140417/scope/tests/applications/user/com.ubuntu.music_music_1.1.329.de0000644000015301777760000000101612323764232033060 0ustar pbusernogroup00000000000000[Desktop Entry] Version=1.0 Name=Music Comment=Ubuntu Touch Music Player Exec=aa-exec-click -p com.ubuntu.music_music_1.1.329 -- qmlscene ./music-app.qml --file=%f -I ./plugins Icon=/usr/share/click/preinstalled/.click/users/@all/com.ubuntu.music/images/music.png Terminal=false Type=Application StartupNotify=true X-Ubuntu-Single-Instance=true X-Ubuntu-Touch=true Path=/usr/share/click/preinstalled/.click/users/@all/com.ubuntu.music X-Ubuntu-Old-Icon=images/music.png X-Ubuntu-Application-ID=com.ubuntu.music_music_1.1.329 ././@LongLink0000000000000000000000000000015600000000000011217 Lustar 00000000000000unity-scope-click-0.1+14.04.20140417/scope/tests/applications/user/com.ubuntu.weather_weather_1.0.168.desktopunity-scope-click-0.1+14.04.20140417/scope/tests/applications/user/com.ubuntu.weather_weather_1.0.160000644000015301777760000000105012323764232033235 0ustar pbusernogroup00000000000000[Desktop Entry] Version=1.0 Type=Application Terminal=false Exec=aa-exec-click -p com.ubuntu.weather_weather_1.0.168 -- qmlscene ./ubuntu-weather-app.qml Icon=/usr/share/click/preinstalled/.click/users/@all/com.ubuntu.weather/./weather64.png Name=Weather X-Ubuntu-Touch=true X-Ubuntu-StageHint=SideStage X-Ubuntu-Gettext-Domain=com.ubuntu.weather # Fake an app installed from a .deb #Path=/usr/share/click/preinstalled/.click/users/@all/com.ubuntu.weather #X-Ubuntu-Old-Icon=./weather64.png #X-Ubuntu-Application-ID=com.ubuntu.weather_weather_1.0.168 ././@LongLink0000000000000000000000000000020300000000000011210 Lustar 00000000000000unity-scope-click-0.1+14.04.20140417/scope/tests/applications/user/com.ubuntu.dropping-letters_dropping-letters_0.1.2.2.43.desktopunity-scope-click-0.1+14.04.20140417/scope/tests/applications/user/com.ubuntu.dropping-letters_dropp0000644000015301777760000000104112323764232033762 0ustar pbusernogroup00000000000000[Desktop Entry] Version=1.0 Type=Application Icon=/usr/share/click/preinstalled/.click/users/@all/com.ubuntu.dropping-letters/dropping-letters.png Terminal=false Name=Dropping Letters Exec=aa-exec-click -p com.ubuntu.dropping-letters_dropping-letters_0.1.2.2.43 -- qmlscene dropping-letters.qml X-Ubuntu-Touch=true X-Ubuntu-StageHint=SideStage Path=/usr/share/click/preinstalled/.click/users/@all/com.ubuntu.dropping-letters X-Ubuntu-Old-Icon=dropping-letters.png X-Ubuntu-Application-ID=com.ubuntu.dropping-letters_dropping-letters_0.1.2.2.43 ././@LongLink0000000000000000000000000000015200000000000011213 Lustar 00000000000000unity-scope-click-0.1+14.04.20140417/scope/tests/applications/user/non-click-app-onlyshowin-gnome.desktopunity-scope-click-0.1+14.04.20140417/scope/tests/applications/user/non-click-app-onlyshowin-gnome.de0000644000015301777760000000047212323764232033521 0ustar pbusernogroup00000000000000[Desktop Entry] Type=Application Name=Sample Desktop-only non-click app GenericName=NonClickAppWithoutException Comment=multiline description goes here Exec=messaging-app %u Terminal=false MimeType=x-scheme-handler/contact;x-scheme-handler/call OnlyShowIn=GNOME; Name[en_US]=non-click-app-onlyshowin-gnome.desktop ././@LongLink0000000000000000000000000000016000000000000011212 Lustar 00000000000000unity-scope-click-0.1+14.04.20140417/scope/tests/applications/user/com.ubuntu.calendar_calendar_0.4.182.desktopunity-scope-click-0.1+14.04.20140417/scope/tests/applications/user/com.ubuntu.calendar_calendar_0.4.0000644000015301777760000000100412323764232033314 0ustar pbusernogroup00000000000000[Desktop Entry] Version=1.0 Type=Application Terminal=false Exec=aa-exec-click -p com.ubuntu.calendar_calendar_0.4.182 -- qmlscene %u ./calendar.qml Icon=/usr/share/click/preinstalled/.click/users/@all/com.ubuntu.calendar/./calendar64.png Name=Calendar X-Ubuntu-Touch=true X-Ubuntu-StageHint=SideStage X-Ubuntu-Gettext-Domain=com.ubuntu.calendar Path=/usr/share/click/preinstalled/.click/users/@all/com.ubuntu.calendar X-Ubuntu-Old-Icon=./calendar64.png X-Ubuntu-Application-ID=com.ubuntu.calendar_calendar_0.4.182 unity-scope-click-0.1+14.04.20140417/scope/tests/applications/user/non-click-app-nodisplay.desktop0000644000015301777760000000040412323764232033263 0ustar pbusernogroup00000000000000[Desktop Entry] Type=Application Name=Sample Desktop-only non-click app GenericName=NonClickAppWithoutException Comment=multiline description goes here Exec=messaging-app %u Terminal=false MimeType=x-scheme-handler/contact;x-scheme-handler/call NoDisplay=true ././@LongLink0000000000000000000000000000021000000000000011206 Lustar 00000000000000unity-scope-click-0.1+14.04.20140417/scope/tests/applications/user/com.ubuntu.developer.webapps.webapp-gmail_webapp-gmail_1.0.8.desktopunity-scope-click-0.1+14.04.20140417/scope/tests/applications/user/com.ubuntu.developer.webapps.weba0000644000015301777760000000116012323764232033620 0ustar pbusernogroup00000000000000[Desktop Entry] Type=Application Terminal=false Exec=aa-exec-click -p com.ubuntu.developer.webapps.webapp-gmail_webapp-gmail_1.0.8 -- webbrowser-app --enable-back-forward --webappModelSearchPath=. --webapp='R01haWwNCg==' https://mail.google.com/ Name=Gmail Icon=/usr/share/click/preinstalled/.click/users/@all/com.ubuntu.developer.webapps.webapp-gmail/./gmail.png X-Ubuntu-Touch=true X-Ubuntu-Single-Instance=true Path=/usr/share/click/preinstalled/.click/users/@all/com.ubuntu.developer.webapps.webapp-gmail X-Ubuntu-Old-Icon=./gmail.png X-Ubuntu-Application-ID=com.ubuntu.developer.webapps.webapp-gmail_webapp-gmail_1.0.8 ././@LongLink0000000000000000000000000000020600000000000011213 Lustar 00000000000000unity-scope-click-0.1+14.04.20140417/scope/tests/applications/user/com.ubuntu.developer.webapps.webapp-ebay_webapp-ebay_1.0.8.desktopunity-scope-click-0.1+14.04.20140417/scope/tests/applications/user/com.ubuntu.developer.webapps.weba0000644000015301777760000000114212323764232033620 0ustar pbusernogroup00000000000000[Desktop Entry] Type=Application Terminal=false Exec=aa-exec-click -p com.ubuntu.developer.webapps.webapp-ebay_webapp-ebay_1.0.8 -- webbrowser-app --enable-back-forward --webappUrlPatterns=https?://*.ebay.com/* --webapp http://m.ebay.com/ Name=eBay Icon=/usr/share/click/preinstalled/.click/users/@all/com.ubuntu.developer.webapps.webapp-ebay/./ebay.png X-Ubuntu-Touch=true X-Ubuntu-Single-Instance=true Path=/usr/share/click/preinstalled/.click/users/@all/com.ubuntu.developer.webapps.webapp-ebay X-Ubuntu-Old-Icon=./ebay.png X-Ubuntu-Application-ID=com.ubuntu.developer.webapps.webapp-ebay_webapp-ebay_1.0.8 ././@LongLink0000000000000000000000000000021400000000000011212 Lustar 00000000000000unity-scope-click-0.1+14.04.20140417/scope/tests/applications/user/com.ubuntu.developer.webapps.webapp-twitter_webapp-twitter_1.0.5.desktopunity-scope-click-0.1+14.04.20140417/scope/tests/applications/user/com.ubuntu.developer.webapps.weba0000644000015301777760000000131712323764232033624 0ustar pbusernogroup00000000000000[Desktop Entry] Type=Application Terminal=false Exec=aa-exec-click -p com.ubuntu.developer.webapps.webapp-twitter_webapp-twitter_1.0.5 -- webbrowser-app --enable-back-forward --webapp --webappUrlPatterns=https?://mobile.twitter.com/* https://mobile.twitter.com/session/new?bypass_interstitial=true Name=Twitter Icon=/usr/share/click/preinstalled/.click/users/@all/com.ubuntu.developer.webapps.webapp-twitter/./twitter.png X-Ubuntu-Touch=true X-Ubuntu-StageHint=SideStage X-Ubuntu-Single-Instance=true Path=/usr/share/click/preinstalled/.click/users/@all/com.ubuntu.developer.webapps.webapp-twitter X-Ubuntu-Old-Icon=./twitter.png X-Ubuntu-Application-ID=com.ubuntu.developer.webapps.webapp-twitter_webapp-twitter_1.0.5 ././@LongLink0000000000000000000000000000016000000000000011212 Lustar 00000000000000unity-scope-click-0.1+14.04.20140417/scope/tests/applications/user/non-click-app-onlyshowin-gnome-unity.desktopunity-scope-click-0.1+14.04.20140417/scope/tests/applications/user/non-click-app-onlyshowin-gnome-un0000644000015301777760000000050612323764232033550 0ustar pbusernogroup00000000000000[Desktop Entry] Type=Application Name=Sample Desktop-only non-click app GenericName=NonClickAppWithoutException Comment=multiline description goes here Exec=messaging-app %u Terminal=false MimeType=x-scheme-handler/contact;x-scheme-handler/call OnlyShowIn=GNOME;Unity; Name[en_US]=non-click-app-onlyshowin-gnome-unity.desktop ././@LongLink0000000000000000000000000000016600000000000011220 Lustar 00000000000000unity-scope-click-0.1+14.04.20140417/scope/tests/applications/user/com.ubuntu.calculator_calculator_0.1.3.206.desktopunity-scope-click-0.1+14.04.20140417/scope/tests/applications/user/com.ubuntu.calculator_calculator_0000644000015301777760000000104612323764232033762 0ustar pbusernogroup00000000000000[Desktop Entry] Version=1.0 Type=Application Terminal=false Exec=aa-exec-click -p com.ubuntu.calculator_calculator_0.1.3.206 -- qmlscene ./ubuntu-calculator-app.qml Icon=/usr/share/click/preinstalled/.click/users/@all/com.ubuntu.calculator/./calculator64.png Name=Calculator X-Ubuntu-Touch=true X-Ubuntu-StageHint=SideStage X-Ubuntu-Gettext-Domain=com.ubuntu.calculator Path=/usr/share/click/preinstalled/.click/users/@all/com.ubuntu.calculator X-Ubuntu-Old-Icon=./calculator64.png X-Ubuntu-Application-ID=com.ubuntu.calculator_calculator_0.1.3.206 ././@LongLink0000000000000000000000000000015700000000000011220 Lustar 00000000000000unity-scope-click-0.1+14.04.20140417/scope/tests/applications/user/com.ubuntu.terminal_terminal_0.5.29.desktopunity-scope-click-0.1+14.04.20140417/scope/tests/applications/user/com.ubuntu.terminal_terminal_0.5.0000644000015301777760000000075312323764232033433 0ustar pbusernogroup00000000000000[Desktop Entry] Version=1.0 Type=Application Terminal=false Exec=aa-exec-click -p com.ubuntu.terminal_terminal_0.5.29 -- qmlscene ./ubuntu-terminal-app.qml -I ./plugins Icon=/usr/share/click/preinstalled/.click/users/@all/com.ubuntu.terminal/./terminal64.png Name=Terminal X-Ubuntu-Touch=true X-Ubuntu-StageHint=SideStage Path=/usr/share/click/preinstalled/.click/users/@all/com.ubuntu.terminal X-Ubuntu-Old-Icon=./terminal64.png X-Ubuntu-Application-ID=com.ubuntu.terminal_terminal_0.5.29 ././@LongLink0000000000000000000000000000020700000000000011214 Lustar 00000000000000unity-scope-click-0.1+14.04.20140417/scope/tests/applications/user/com.ubuntu.stock-ticker-mobile_stock-ticker-mobile_0.3.7.66.desktopunity-scope-click-0.1+14.04.20140417/scope/tests/applications/user/com.ubuntu.stock-ticker-mobile_st0000644000015301777760000000125612323764232033641 0ustar pbusernogroup00000000000000[Desktop Entry] Version=1.0 Name=Stock Ticker GenericName=Stock Ticker Comment=An awesome Stock Ticker application with all the features you could imagine Exec=aa-exec-click -p com.ubuntu.stock-ticker-mobile_stock-ticker-mobile_0.3.7.66 -- qmlscene Stock_Ticker.qml Icon=/usr/share/click/preinstalled/.click/users/@all/com.ubuntu.stock-ticker-mobile/icons/stock_icon_48.png Terminal=false Type=Application X-Ubuntu-Touch=true X-Ubuntu-StageHint=SideStage Categories=Utility; Path=/usr/share/click/preinstalled/.click/users/@all/com.ubuntu.stock-ticker-mobile X-Ubuntu-Old-Icon=icons/stock_icon_48.png X-Ubuntu-Application-ID=com.ubuntu.stock-ticker-mobile_stock-ticker-mobile_0.3.7.66 ././@LongLink0000000000000000000000000000015400000000000011215 Lustar 00000000000000unity-scope-click-0.1+14.04.20140417/scope/tests/applications/user/com.ubuntu.shorts_shorts_0.2.162.desktopunity-scope-click-0.1+14.04.20140417/scope/tests/applications/user/com.ubuntu.shorts_shorts_0.2.162.0000644000015301777760000000075112323764232033153 0ustar pbusernogroup00000000000000[Desktop Entry] Version=1.0 Type=Application Terminal=false Exec=aa-exec-click -p com.ubuntu.shorts_shorts_0.2.162 -- qmlscene ./rssreader-app.qml Icon=/usr/share/click/preinstalled/.click/users/@all/com.ubuntu.shorts/./rssreader64.png Name=Shorts Keywords=shorts;rss;reader X-Ubuntu-Touch=true X-Ubuntu-StageHint=SideStage Path=/usr/share/click/preinstalled/.click/users/@all/com.ubuntu.shorts X-Ubuntu-Old-Icon=./rssreader64.png X-Ubuntu-Application-ID=com.ubuntu.shorts_shorts_0.2.162 ././@LongLink0000000000000000000000000000015200000000000011213 Lustar 00000000000000unity-scope-click-0.1+14.04.20140417/scope/tests/applications/user/com.ubuntu.clock_clock_1.0.300.desktopunity-scope-click-0.1+14.04.20140417/scope/tests/applications/user/com.ubuntu.clock_clock_1.0.300.de0000644000015301777760000000075312323764232033001 0ustar pbusernogroup00000000000000[Desktop Entry] Version=1.0 Type=Application Terminal=false Exec=aa-exec-click -p com.ubuntu.clock_clock_1.0.300 -- qmlscene ./ubuntu-clock-app.qml Icon=/usr/share/click/preinstalled/.click/users/@all/com.ubuntu.clock/./clock64.png Name=Clock X-Ubuntu-Touch=true X-Ubuntu-StageHint=SideStage X-Ubuntu-Gettext-Domain=com.ubuntu.clock Path=/usr/share/click/preinstalled/.click/users/@all/com.ubuntu.clock X-Ubuntu-Old-Icon=./clock64.png X-Ubuntu-Application-ID=com.ubuntu.clock_clock_1.0.300 ././@LongLink0000000000000000000000000000015200000000000011213 Lustar 00000000000000unity-scope-click-0.1+14.04.20140417/scope/tests/applications/user/non-click-app-onlyshowin-unity.desktopunity-scope-click-0.1+14.04.20140417/scope/tests/applications/user/non-click-app-onlyshowin-unity.de0000644000015301777760000000047212323764232033564 0ustar pbusernogroup00000000000000[Desktop Entry] Type=Application Name=Sample Desktop-only non-click app GenericName=NonClickAppWithoutException Comment=multiline description goes here Exec=messaging-app %u Terminal=false MimeType=x-scheme-handler/contact;x-scheme-handler/call OnlyShowIn=Unity; Name[en_US]=non-click-app-onlyshowin-unity.desktop ././@LongLink0000000000000000000000000000016700000000000011221 Lustar 00000000000000unity-scope-click-0.1+14.04.20140417/scope/tests/applications/user/com.ubuntu.filemanager_filemanager_0.1.1.97.desktopunity-scope-click-0.1+14.04.20140417/scope/tests/applications/user/com.ubuntu.filemanager_filemanage0000644000015301777760000000101612323764232033700 0ustar pbusernogroup00000000000000[Desktop Entry] Version=1.0 Type=Application Terminal=false Exec=aa-exec-click -p com.ubuntu.filemanager_filemanager_0.1.1.97 -- qmlscene ./ubuntu-filemanager-app.qml -I ./plugins Icon=/usr/share/click/preinstalled/.click/users/@all/com.ubuntu.filemanager/./filemanager64.png Name=File Manager X-Ubuntu-Touch=true X-Ubuntu-StageHint=SideStage Path=/usr/share/click/preinstalled/.click/users/@all/com.ubuntu.filemanager X-Ubuntu-Old-Icon=./filemanager64.png X-Ubuntu-Application-ID=com.ubuntu.filemanager_filemanager_0.1.1.97 unity-scope-click-0.1+14.04.20140417/scope/tests/applications/system/0000755000015301777760000000000012323764741025612 5ustar pbusernogroup00000000000000unity-scope-click-0.1+14.04.20140417/scope/tests/applications/system/messaging-app.desktop0000644000015301777760000000062212323764232031733 0ustar pbusernogroup00000000000000[Desktop Entry] Type=Application Name=Messaging GenericName=Messaging Comment=Messaging application Exec=messaging-app %u Terminal=false Icon=messages-app MimeType=x-scheme-handler/contact;x-scheme-handler/call X-Ubuntu-Touch=true X-Ubuntu-StageHint=SideStage X-Ubuntu-Gettext-Domain=messaging-app X-Ubuntu-Single-Instance=true X-Screenshot=/usr/share/messaging-app/assets/messaging-app-screenshot.png unity-scope-click-0.1+14.04.20140417/scope/tests/applications/system/address-book-app.desktop0000644000015301777760000000053212323764232032333 0ustar pbusernogroup00000000000000[Desktop Entry] Encoding=UTF-8 Version=1.0 Terminal=false Type=Application Name=Contacts GenericName=Contacts Exec=/usr/bin/address-book-app %u Icon=contacts-app X-Ubuntu-Touch=true X-Ubuntu-StageHint=SideStage X-Ubuntu-Gettext-Domain=address-book-app X-Ubuntu-Single-Instance=true # this one has no screenshot nor comment, to test how it breaks unity-scope-click-0.1+14.04.20140417/scope/tests/mock_ubuntuone_credentials.h0000644000015301777760000000304612323764232027361 0ustar pbusernogroup00000000000000/* * Copyright (C) 2014 Canonical Ltd. * * This program is free software: you can redistribute it and/or modify it * under the terms of the GNU General Public License version 3, as published * by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranties of * MERCHANTABILITY, SATISFACTORY QUALITY, or FITNESS FOR A PARTICULAR * PURPOSE. See the GNU General Public License for more details. * * You should have received a copy of the GNU General Public License along * with this program. If not, see . * * In addition, as a special exception, the copyright holders give * permission to link the code of portions of this program with the * OpenSSL library under certain conditions as described in each * individual source file, and distribute linked combinations * including the two. * You must obey the GNU General Public License in all respects * for all of the code used other than OpenSSL. If you modify * file(s) with this exception, you may extend this exception to your * version of the file(s), but you are not obligated to do so. If you * do not wish to do so, delete this exception statement from your * version. If you delete this exception statement from all source * files in the program, then also delete it here. */ class MockCredentialsService : public click::CredentialsService { public: MOCK_METHOD0(getCredentials, void()); MOCK_METHOD0(invalidateCredentials, void()); }; unity-scope-click-0.1+14.04.20140417/scope/tests/test_smartconnect.cpp0000644000015301777760000000724412323764232026045 0ustar pbusernogroup00000000000000/* * Copyright (C) 2014 Canonical Ltd. * * This program is free software: you can redistribute it and/or modify it * under the terms of the GNU General Public License version 3, as published * by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranties of * MERCHANTABILITY, SATISFACTORY QUALITY, or FITNESS FOR A PARTICULAR * PURPOSE. See the GNU General Public License for more details. * * You should have received a copy of the GNU General Public License along * with this program. If not, see . * * In addition, as a special exception, the copyright holders give * permission to link the code of portions of this program with the * OpenSSL library under certain conditions as described in each * individual source file, and distribute linked combinations * including the two. * You must obey the GNU General Public License in all respects * for all of the code used other than OpenSSL. If you modify * file(s) with this exception, you may extend this exception to your * version of the file(s), but you are not obligated to do so. If you * do not wish to do so, delete this exception statement from your * version. If you delete this exception statement from all source * files in the program, then also delete it here. */ #include "click/smartconnect.h" #include #include using namespace ::testing; namespace { class FakeObject : public QObject { Q_OBJECT public: explicit FakeObject(QObject* parent=0) : QObject(parent) {} signals: void signal0(); void signal1(int x); void signal2(int x, QString y); }; class SmartConnectTest : public Test, QObject { public: MOCK_METHOD0(handler0, void()); MOCK_METHOD1(handler1, void(int x)); MOCK_METHOD2(handler2, void(int x, QString y)); }; } // namespace TEST_F(SmartConnectTest, testSlotNoArgsCalled) { click::utils::SmartConnect sc; FakeObject* o = new FakeObject(&sc); sc.connect(o, &FakeObject::signal0, [=](){ handler0(); }); EXPECT_CALL(*this, handler0()).Times(1); emit o->signal0(); } TEST_F(SmartConnectTest, testSlotOneArgCalled) { click::utils::SmartConnect sc; FakeObject* o = new FakeObject(&sc); sc.connect(o, &FakeObject::signal1, [=](int x){ handler1(x); }); EXPECT_CALL(*this, handler1(42)).Times(1); emit o->signal1(42); } TEST_F(SmartConnectTest, testSlotTwoArgsCalled) { click::utils::SmartConnect sc; FakeObject* o = new FakeObject(&sc); sc.connect(o, &FakeObject::signal2, [=](int x, QString y){ handler2(x, y); }); EXPECT_CALL(*this, handler2(42, QString("Blue"))).Times(1); emit o->signal2(42, "Blue"); } TEST_F(SmartConnectTest, testDisconnectsOnFirstCalled) { click::utils::SmartConnect sc; FakeObject* o = new FakeObject(&sc); sc.connect(o, &FakeObject::signal2, [=](int x, QString y){ handler2(x, y); }); sc.connect(o, &FakeObject::signal1, [=](int x){ handler1(x); }); EXPECT_CALL(*this, handler2(42, QString("Blue"))).Times(1); EXPECT_CALL(*this, handler1(_)).Times(0); emit o->signal2(42, "Blue"); emit o->signal0(); emit o->signal1(83); emit o->signal2(83, "Red"); } class FakeSmartConnect : public click::utils::SmartConnect { Q_OBJECT public: MOCK_METHOD0(cleanup, void()); }; TEST_F(SmartConnectTest, testCleanupCalledOnDisconnect) { FakeSmartConnect fsc; FakeObject* o = new FakeObject(&fsc); fsc.connect(o, &FakeObject::signal0, [](){}); EXPECT_CALL(fsc, cleanup()).Times(1); emit o->signal0(); } #include "test_smartconnect.moc" unity-scope-click-0.1+14.04.20140417/scope/tests/test_data.cpp0000644000015301777760000000000012323764232024235 0ustar pbusernogroup00000000000000unity-scope-click-0.1+14.04.20140417/scope/tests/mock_ubuntu_download_manager.h0000644000015301777760000000742612323764232027671 0ustar pbusernogroup00000000000000/* * Copyright (C) 2014 Canonical Ltd. * * This program is free software: you can redistribute it and/or modify it * under the terms of the GNU General Public License version 3, as published * by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranties of * MERCHANTABILITY, SATISFACTORY QUALITY, or FITNESS FOR A PARTICULAR * PURPOSE. See the GNU General Public License for more details. * * You should have received a copy of the GNU General Public License along * with this program. If not, see . * * In addition, as a special exception, the copyright holders give * permission to link the code of portions of this program with the * OpenSSL library under certain conditions as described in each * individual source file, and distribute linked combinations * including the two. * You must obey the GNU General Public License in all respects * for all of the code used other than OpenSSL. If you modify * file(s) with this exception, you may extend this exception to your * version of the file(s), but you are not obligated to do so. If you * do not wish to do so, delete this exception statement from your * version. If you delete this exception statement from all source * files in the program, then also delete it here. */ #ifndef _MOCK_UBUNTU_DOWNLOAD_MANAGER_H_ #define _MOCK_UBUNTU_DOWNLOAD_MANAGER_H_ #include #include #include #include #include class MockDownload : public Ubuntu::DownloadManager::Download { public: MockDownload() : Ubuntu::DownloadManager::Download(){}; MockDownload(Ubuntu::DownloadManager::Error *err) : Ubuntu::DownloadManager::Download(err) {}; // mock ALL methods so that we do not have an abstract class MOCK_METHOD0(start, void()); MOCK_METHOD0(pause, void()); MOCK_METHOD0(resume, void()); MOCK_METHOD0(cancel, void()); MOCK_METHOD1(allowMobileDownload, void(bool)); MOCK_METHOD0(isMobileDownloadAllowed, bool()); MOCK_METHOD1(setThrottle, void(qulonglong)); MOCK_METHOD0(throttle, qulonglong()); MOCK_CONST_METHOD0(id, QString()); MOCK_METHOD0(metadata, QVariantMap()); MOCK_METHOD0(progress, qulonglong()); MOCK_METHOD0(totalSize, qulonglong()); MOCK_CONST_METHOD0(isError, bool()); MOCK_CONST_METHOD0(error, Error*()); }; class MockError : public Ubuntu::DownloadManager::Error { public: MockError() : Ubuntu::DownloadManager::Error(Ubuntu::DownloadManager::Error::Type::Process, 0) {}; MOCK_METHOD0(errorString, QString()); }; class MockSystemDownloadManager : public Ubuntu::DownloadManager::Manager { public: MockSystemDownloadManager() : Ubuntu::DownloadManager::Manager() {}; MOCK_METHOD1(createDownload, void(DownloadStruct downStruct)); MOCK_METHOD3(createDownload, void(DownloadStruct downStruct, DownloadCb cb, DownloadCb errCb)); MOCK_METHOD5(createDownload, void(StructList downs, const QString &algorithm, bool allowed3G, const QVariantMap &metadata, StringMap headers)); MOCK_METHOD7(createDownload, void(StructList downs, const QString &algorithm, bool allowed3G, const QVariantMap &metadata, StringMap headers, GroupCb cb, GroupCb errCb)); MOCK_CONST_METHOD0(isError, bool()); MOCK_CONST_METHOD0(lastError, Error*()); MOCK_METHOD1(allowMobileDataDownload, void(bool)); MOCK_METHOD0(isMobileDataDownload, bool()); MOCK_METHOD0(defaultThrottle, qulonglong()); MOCK_METHOD1(setDefaultThrottle, void(qulonglong)); MOCK_METHOD0(exit, void()); }; #endif /* _MOCK_UBUNTU_DOWNLOAD_MANAGER_H_ */ unity-scope-click-0.1+14.04.20140417/scope/tests/test_interface.cpp0000644000015301777760000003523412323764232025305 0ustar pbusernogroup00000000000000/* * Copyright (C) 2014 Canonical Ltd. * * This program is free software: you can redistribute it and/or modify it * under the terms of the GNU General Public License version 3, as published * by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranties of * MERCHANTABILITY, SATISFACTORY QUALITY, or FITNESS FOR A PARTICULAR * PURPOSE. See the GNU General Public License for more details. * * You should have received a copy of the GNU General Public License along * with this program. If not, see . * * In addition, as a special exception, the copyright holders give * permission to link the code of portions of this program with the * OpenSSL library under certain conditions as described in each * individual source file, and distribute linked combinations * including the two. * You must obey the GNU General Public License in all respects * for all of the code used other than OpenSSL. If you modify * file(s) with this exception, you may extend this exception to your * version of the file(s), but you are not obligated to do so. If you * do not wish to do so, delete this exception statement from your * version. If you delete this exception statement from all source * files in the program, then also delete it here. */ #include "test_data.h" #include #include #include #include #include #include #include #include #include using namespace click; namespace { // TODO: Get rid of file-based testing and instead make unity::util::IniParser mockable // Maintaining this list here will become tedious over time. static const std::vector non_desktop_applications = { {"com.ubuntu.developer.webapps.webapp-ubuntuone", "Ubuntu One", 0.0, "/usr/share/click/preinstalled/.click/users/@all/com.ubuntu.developer.webapps.webapp-ubuntuone/./ubuntuone.png", "application:///com.ubuntu.developer.webapps.webapp-ubuntuone_webapp-ubuntuone_1.0.4.desktop", "", ""}, {"com.ubuntu.stock-ticker-mobile", "Stock Ticker", 0.0, "/usr/share/click/preinstalled/.click/users/@all/com.ubuntu.stock-ticker-mobile/icons/stock_icon_48.png", "application:///com.ubuntu.stock-ticker-mobile_stock-ticker-mobile_0.3.7.66.desktop", "", ""}, {"", "Weather", 0.0, "/usr/share/click/preinstalled/.click/users/@all/com.ubuntu.weather/./weather64.png", "application:///com.ubuntu.weather_weather_1.0.168.desktop", "", ""}, {"com.ubuntu.developer.webapps.webapp-twitter", "Twitter", 0.0, "/usr/share/click/preinstalled/.click/users/@all/com.ubuntu.developer.webapps.webapp-twitter/./twitter.png", "application:///com.ubuntu.developer.webapps.webapp-twitter_webapp-twitter_1.0.5.desktop", "", ""}, {"com.ubuntu.music", "Music", 0.0, "/usr/share/click/preinstalled/.click/users/@all/com.ubuntu.music/images/music.png", "application:///com.ubuntu.music_music_1.1.329.desktop", "", ""}, {"com.ubuntu.clock", "Clock", 0.0, "/usr/share/click/preinstalled/.click/users/@all/com.ubuntu.clock/./clock64.png", "application:///com.ubuntu.clock_clock_1.0.300.desktop", "", ""}, {"com.ubuntu.dropping-letters", "Dropping Letters", 0.0, "/usr/share/click/preinstalled/.click/users/@all/com.ubuntu.dropping-letters/dropping-letters.png", "application:///com.ubuntu.dropping-letters_dropping-letters_0.1.2.2.43.desktop", "", ""}, {"com.ubuntu.developer.webapps.webapp-gmail", "Gmail", 0.0, "/usr/share/click/preinstalled/.click/users/@all/com.ubuntu.developer.webapps.webapp-gmail/./gmail.png", "application:///com.ubuntu.developer.webapps.webapp-gmail_webapp-gmail_1.0.8.desktop", "", ""}, {"com.ubuntu.terminal", "Terminal", 0.0, "/usr/share/click/preinstalled/.click/users/@all/com.ubuntu.terminal/./terminal64.png", "application:///com.ubuntu.terminal_terminal_0.5.29.desktop", "", ""}, {"com.ubuntu.calendar", "Calendar", 0.0, "/usr/share/click/preinstalled/.click/users/@all/com.ubuntu.calendar/./calendar64.png", "application:///com.ubuntu.calendar_calendar_0.4.182.desktop", "", ""}, {"com.ubuntu.notes", "Notes", 0.0, "image://theme/notepad", "application:///com.ubuntu.notes_notes_1.4.242.desktop", "", ""}, {"com.ubuntu.developer.webapps.webapp-amazon", "Amazon", 0.0, "/usr/share/click/preinstalled/.click/users/@all/com.ubuntu.developer.webapps.webapp-amazon/./amazon.png", "application:///com.ubuntu.developer.webapps.webapp-amazon_webapp-amazon_1.0.6.desktop", "", ""}, {"com.ubuntu.shorts", "Shorts", 0.0, "/usr/share/click/preinstalled/.click/users/@all/com.ubuntu.shorts/./rssreader64.png", "application:///com.ubuntu.shorts_shorts_0.2.162.desktop", "", ""}, {"com.ubuntu.filemanager", "File Manager", 0.0, "/usr/share/click/preinstalled/.click/users/@all/com.ubuntu.filemanager/./filemanager64.png", "application:///com.ubuntu.filemanager_filemanager_0.1.1.97.desktop", "", ""}, {"com.ubuntu.calculator", "Calculator", 0.0, "/usr/share/click/preinstalled/.click/users/@all/com.ubuntu.calculator/./calculator64.png", "application:///com.ubuntu.calculator_calculator_0.1.3.206.desktop", "", ""}, {"com.ubuntu.sudoku", "Sudoku", 0.0, "/usr/share/click/preinstalled/.click/users/@all/com.ubuntu.sudoku/SudokuGameIcon.png", "application:///com.ubuntu.sudoku_sudoku_1.0.142.desktop", "", ""}, {"com.ubuntu.developer.webapps.webapp-ebay", "eBay", 0.0, "/usr/share/click/preinstalled/.click/users/@all/com.ubuntu.developer.webapps.webapp-ebay/./ebay.png", "application:///com.ubuntu.developer.webapps.webapp-ebay_webapp-ebay_1.0.8.desktop", "", ""}, {"com.ubuntu.developer.webapps.webapp-facebook", "Facebook", 0.0, "/usr/share/click/preinstalled/.click/users/@all/com.ubuntu.developer.webapps.webapp-facebook/./facebook.png", "application:///com.ubuntu.developer.webapps.webapp-facebook_webapp-facebook_1.0.5.desktop", "", ""}, {"", "Messaging", 0.0, "image://theme/messages-app", "application:///messaging-app.desktop", "Messaging application", "/usr/share/messaging-app/assets/messaging-app-screenshot.png"}, {"", "Contacts", 0.0, "image://theme/contacts-app", "application:///address-book-app.desktop", "", ""} }; static click::Application desktop_application { "", "Sample Desktop-only non-click app", 0.0, "image://theme/sample-desktop-app", "application:///non-click-app-without-exception.desktop", "multiline description goes here", "" }; } namespace { const QString emptyQuery{}; struct MockKeyFileLocator : public click::KeyFileLocator { typedef click::KeyFileLocator Super; MockKeyFileLocator() { using namespace ::testing; ON_CALL(*this, enumerateKeyFilesForInstalledApplications(_)) .WillByDefault( Invoke( this, &MockKeyFileLocator::doEnumerateKeyFilesForInstalledApplications)); } MOCK_METHOD1(enumerateKeyFilesForInstalledApplications, void(const Super::Enumerator&)); void doEnumerateKeyFilesForInstalledApplications(const Super::Enumerator& enumerator) { Super::enumerateKeyFilesForInstalledApplications(enumerator); } }; } class FakeClickInterface : public click::Interface { public: FakeClickInterface(const QSharedPointer& keyFileLocator) : Interface(keyFileLocator) {} MOCK_METHOD0(show_desktop_apps, bool()); }; TEST(ClickInterface, testIsNonClickAppFalse) { EXPECT_FALSE(Interface::is_non_click_app("unknown-app.desktop")); } TEST(ClickInterface, testIsNonClickAppNoRegression) { // Loop through and check that all filenames are non-click filenames // If this ever breaks, something is very very wrong. for (const auto& element : nonClickDesktopFiles()) { QString filename = element.c_str(); EXPECT_TRUE(Interface::is_non_click_app(filename)); } } TEST(ClickInterface, testCallsIntoKeyFileLocatorForFindingInstalledApps) { using namespace ::testing; MockKeyFileLocator mockKeyFileLocator; QSharedPointer keyFileLocator( &mockKeyFileLocator, [](click::KeyFileLocator*){}); FakeClickInterface iface(keyFileLocator); EXPECT_CALL(iface, show_desktop_apps()) .Times(1) .WillOnce(Return(false)); EXPECT_CALL(mockKeyFileLocator, enumerateKeyFilesForInstalledApplications(_)).Times(1); iface.find_installed_apps(emptyQuery); } TEST(ClickInterface, testFindAppsInDirEmpty) { QSharedPointer keyFileLocator( new click::KeyFileLocator( testing::systemApplicationsDirectoryForTesting(), testing::userApplicationsDirectoryForTesting())); click::Interface iface(keyFileLocator); auto results = iface.find_installed_apps("foo"); EXPECT_TRUE(results.empty()); } TEST(ClickInterface, testIsIconIdentifier) { EXPECT_TRUE(Interface::is_icon_identifier("contacts-app")); EXPECT_FALSE(Interface::is_icon_identifier( "/usr/share/unity8/graphics/applicationIcons/contacts-app@18.png")); } TEST(ClickInterface, testAddThemeScheme) { EXPECT_EQ("image://theme/contacts-app", Interface::add_theme_scheme("contacts-app")); EXPECT_EQ("/usr/share/unity8/graphics/applicationIcons/contacts-app@18.png", Interface::add_theme_scheme("/usr/share/unity8/graphics/applicationIcons/contacts-app@18.png")); } std::vector find_installed_apps(QString query, bool include_desktop_results) { using namespace ::testing; QSharedPointer keyFileLocator( new click::KeyFileLocator( testing::systemApplicationsDirectoryForTesting(), testing::userApplicationsDirectoryForTesting())); FakeClickInterface iface(keyFileLocator); EXPECT_CALL(iface, show_desktop_apps()) .Times(1) .WillOnce(Return(include_desktop_results)); return iface.find_installed_apps(query); } TEST(ClickInterface, testFindInstalledAppsOnPhone) { auto result = find_installed_apps(emptyQuery, false); EXPECT_TRUE(result.size() > 0); for (const auto& app : non_desktop_applications) { qDebug() << "comparing" << QString::fromStdString(app.title); EXPECT_NE(result.end(), std::find(result.begin(), result.end(), app)); } EXPECT_EQ(result.end(), std::find(result.begin(), result.end(), desktop_application)); } TEST(ClickInterface, testFindInstalledAppsOnDesktop) { auto result = find_installed_apps(emptyQuery, true); std::vector expected_apps(non_desktop_applications); expected_apps.push_back(desktop_application); EXPECT_TRUE(result.size() > 0); for (const auto& app : expected_apps) { qDebug() << "comparing" << QString::fromStdString(app.title); EXPECT_NE(result.end(), std::find(result.begin(), result.end(), app)); } } TEST(ClickInterface, testIncludeInResultsNoDisplayTrue) { QSharedPointer keyFileLocator(new click::KeyFileLocator()); click::Interface iface(keyFileLocator); auto nodisplay = testing::userApplicationsDirectoryForTesting() + "/non-click-app-nodisplay.desktop"; unity::util::IniParser parser(nodisplay.data()); EXPECT_FALSE(iface.is_visible_app(parser)); } TEST(ClickInterface, testIncludeInResultsOnlyShowInGnome) { QSharedPointer keyFileLocator(new click::KeyFileLocator()); click::Interface iface(keyFileLocator); auto nodisplay = testing::userApplicationsDirectoryForTesting() + "/non-click-app-onlyshowin-gnome.desktop"; unity::util::IniParser parser(nodisplay.data()); EXPECT_FALSE(iface.is_visible_app(parser)); } TEST(ClickInterface, testIncludeInResultsOnlyShowInGnomeUnity) { QSharedPointer keyFileLocator(new click::KeyFileLocator()); click::Interface iface(keyFileLocator); auto nodisplay = testing::userApplicationsDirectoryForTesting() + "/non-click-app-onlyshowin-gnome-unity.desktop"; unity::util::IniParser parser(nodisplay.data()); EXPECT_TRUE(iface.is_visible_app(parser)); } TEST(ClickInterface, testIncludeInResultsOnlyShowInUnity) { QSharedPointer keyFileLocator(new click::KeyFileLocator()); click::Interface iface(keyFileLocator); auto nodisplay = testing::userApplicationsDirectoryForTesting() + "/non-click-app-onlyshowin-unity.desktop"; unity::util::IniParser parser(nodisplay.data()); EXPECT_TRUE(iface.is_visible_app(parser)); } TEST(ClickInterface, testEnableDesktopApps) { QSharedPointer keyFileLocator(new click::KeyFileLocator()); click::Interface iface(keyFileLocator); setenv(Interface::ENV_SHOW_DESKTOP_APPS, "YesPlease", true); EXPECT_TRUE(iface.show_desktop_apps()); } TEST(ClickInterface, testDisableDesktopApps) { QSharedPointer keyFileLocator(new click::KeyFileLocator()); click::Interface iface(keyFileLocator); unsetenv(Interface::ENV_SHOW_DESKTOP_APPS); EXPECT_FALSE(iface.show_desktop_apps()); } class FakeFrameworkLocator : public click::FrameworkLocator { public: MOCK_METHOD2(list_folder, std::vector( const std::string& folder, const std::string& pattern)); }; TEST(FrameworkLocator, getAvailableFrameworksUsesRightFolder) { using namespace ::testing; FakeFrameworkLocator locator; EXPECT_CALL(locator, list_folder(FrameworkLocator::FRAMEWORKS_FOLDER, _)) .Times(1).WillOnce(Return(std::vector())); locator.get_available_frameworks(); } TEST(FrameworkLocator, getAvailableFrameworksUsesRightPattern) { using namespace ::testing; FakeFrameworkLocator locator; EXPECT_CALL(locator, list_folder(_, FrameworkLocator::FRAMEWORKS_PATTERN)) .Times(1).WillOnce(Return(std::vector())); locator.get_available_frameworks(); } TEST(FrameworkLocator, getAvailableFrameworksTwoResults) { using namespace ::testing; FakeFrameworkLocator locator; std::vector response = {"abc.framework", "def.framework"}; EXPECT_CALL(locator, list_folder(_, _)) .Times(1) .WillOnce(Return(response)); auto frameworks = locator.get_available_frameworks(); std::vector expected = {"abc", "def"}; EXPECT_EQ(expected, frameworks); } TEST(FrameworkLocator, getAvailableFrameworksNoResults) { using namespace ::testing; FakeFrameworkLocator locator; std::vector response = {}; EXPECT_CALL(locator, list_folder(_, _)) .Times(1) .WillOnce(Return(response)); auto frameworks = locator.get_available_frameworks(); EXPECT_EQ(0, frameworks.size()); } unity-scope-click-0.1+14.04.20140417/scope/tests/test_webclient.cpp0000644000015301777760000002331612323764232025317 0ustar pbusernogroup00000000000000/* * Copyright (C) 2014 Canonical Ltd. * * This program is free software: you can redistribute it and/or modify it * under the terms of the GNU General Public License version 3, as published * by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranties of * MERCHANTABILITY, SATISFACTORY QUALITY, or FITNESS FOR A PARTICULAR * PURPOSE. See the GNU General Public License for more details. * * You should have received a copy of the GNU General Public License along * with this program. If not, see . * * In addition, as a special exception, the copyright holders give * permission to link the code of portions of this program with the * OpenSSL library under certain conditions as described in each * individual source file, and distribute linked combinations * including the two. * You must obey the GNU General Public License in all respects * for all of the code used other than OpenSSL. If you modify * file(s) with this exception, you may extend this exception to your * version of the file(s), but you are not obligated to do so. If you * do not wish to do so, delete this exception statement from your * version. If you delete this exception statement from all source * files in the program, then also delete it here. */ #include #include "click/webclient.h" #include "mock_network_access_manager.h" #include "mock_ubuntuone_credentials.h" #include const std::string FAKE_SERVER = "http://fake-server/"; const std::string FAKE_PATH = "fake/api/path"; MATCHER_P(IsCorrectUrl, refUrl, "") { *result_listener << "where the url is " << qPrintable(arg.url().toString()); return arg.url().toString() == refUrl; } MATCHER_P(IsValidOAuthHeader, refOAuth, "") { return arg.hasRawHeader("Authorization") && arg.rawHeader(click::web::AUTHORIZATION.c_str()) .startsWith("OAuth "); } MATCHER_P(IsCorrectCookieHeader, refCookie, "") { return arg.hasRawHeader("Cookie") && arg.rawHeader("Cookie") == refCookie; } MATCHER_P(IsCorrectBufferData, refData, "") { return dynamic_cast(arg)->data() == refData; } class WebClientTest : public ::testing::Test { public: MockNetworkAccessManager nam; QSharedPointer namPtr; MockCredentialsService sso; QSharedPointer ssoPtr; void SetUp() { namPtr.reset(&nam, [](click::network::AccessManager*) {}); ssoPtr.reset(&sso, [](click::CredentialsService*) {}); } MOCK_METHOD0(replyHandler, void()); MOCK_METHOD1(errorHandler, void(QString description)); }; TEST_F(WebClientTest, testUrlBuiltNoParams) { using namespace ::testing; auto reply = new NiceMock(); ON_CALL(*reply, readAll()).WillByDefault(Return("HOLA")); QSharedPointer replyPtr(reply); click::web::Client wc(namPtr, ssoPtr); EXPECT_CALL(nam, sendCustomRequest(IsCorrectUrl(QString("http://fake-server/fake/api/path")), _, _)) .Times(1) .WillOnce(Return(replyPtr)); auto wr = wc.call(FAKE_SERVER + FAKE_PATH); } TEST_F(WebClientTest, testParamsAppended) { using namespace ::testing; auto reply = new NiceMock(); ON_CALL(*reply, readAll()).WillByDefault(Return("HOLA")); QSharedPointer replyPtr(reply); click::web::Client wc(namPtr, ssoPtr); click::web::CallParams params; params.add("a", "1"); params.add("b", "2"); EXPECT_CALL(nam, sendCustomRequest(IsCorrectUrl(QString("http://fake-server/fake/api/path?a=1&b=2")), _, _)) .Times(1) .WillOnce(Return(replyPtr)); auto wr = wc.call(FAKE_SERVER + FAKE_PATH, params); } /* TEST(WebClient, testResultsAreEmmited) { FakeNam::scripted_responses.append("HOLA"); click::web::Client wc( FAKE_SERVER, QSharedPointer(new click::network::AccessManager())); auto wr = wc.call(FAKE_SERVER + FAKE_PATH); connect(wr.data(), &click::web::Response::finished, this, &TestWebClient::gotResults); QTRY_COMPARE(results, QString("HOLA")); using namespace ::testing; MockNetworkAccessManager nam; QSharedPointer namPtr( &nam, [](click::network::AccessManager*) {}); auto reply = new NiceMock(); ON_CALL(*reply, readAll()).WillByDefault(Return("HOLA")); click::web::Client wc(namPtr); auto wr = wc.call(FAKE_SERVER + FAKE_PATH); // TODO: We need to extend the web::Response class to allow for reading the contents of the response // EXPECT_EQ(QByteArray("HOLA"), wr->); } */ TEST_F(WebClientTest, testCookieHeaderSetCorrectly) { using namespace ::testing; auto reply = new NiceMock(); ON_CALL(*reply, readAll()).WillByDefault(Return("HOLA")); QSharedPointer replyPtr(reply); click::web::Client wc(namPtr, ssoPtr); EXPECT_CALL(nam, sendCustomRequest(IsCorrectCookieHeader("CookieCookieCookie"), _, _)) .Times(1) .WillOnce(Return(replyPtr)); auto wr = wc.call(FAKE_SERVER + FAKE_PATH, "GET", false, std::map({{"Cookie", "CookieCookieCookie"}})); } TEST_F(WebClientTest, testMethodPassedCorrectly) { using namespace ::testing; auto reply = new NiceMock(); ON_CALL(*reply, readAll()).WillByDefault(Return("HOLA")); QSharedPointer replyPtr(reply); click::web::Client wc(namPtr, ssoPtr); QByteArray verb("POST", 4); EXPECT_CALL(nam, sendCustomRequest(_, verb, _)) .Times(1) .WillOnce(Return(replyPtr)); auto wr = wc.call(FAKE_SERVER + FAKE_PATH, "POST", false); } TEST_F(WebClientTest, testBufferDataPassedCorrectly) { using namespace ::testing; auto reply = new NiceMock(); ON_CALL(*reply, readAll()).WillByDefault(Return("HOLA \u5c0f\u6d77")); QSharedPointer replyPtr(reply); click::web::Client wc(namPtr, ssoPtr); EXPECT_CALL(nam, sendCustomRequest(_, _, IsCorrectBufferData("HOLA \u5c0f\u6d77"))) .Times(1) .WillOnce(Return(replyPtr)); auto wr = wc.call(FAKE_SERVER + FAKE_PATH, "POST", false, std::map(), "HOLA \u5c0f\u6d77"); } TEST_F(WebClientTest, testSignedCorrectly) { using namespace ::testing; auto reply = new NiceMock(); ON_CALL(*reply, readAll()).WillByDefault(Return("HOLA")); QSharedPointer replyPtr(reply); click::web::Client wc(namPtr, ssoPtr); EXPECT_CALL(sso, getCredentials()).WillOnce(Invoke([&](){ UbuntuOne::Token token("token_key", "token_secret", "consumer_key", "consumer_secret"); sso.credentialsFound(token); })); EXPECT_CALL(nam, sendCustomRequest(IsValidOAuthHeader(""), _, _)) .Times(1) .WillOnce(Return(replyPtr)); auto wr = wc.call(FAKE_SERVER + FAKE_PATH, "HEAD", true); } TEST_F(WebClientTest, testSignTokenNotFound) { using namespace ::testing; auto reply = new NiceMock(); ON_CALL(*reply, readAll()).WillByDefault(Return("HOLA")); QSharedPointer replyPtr(reply); click::web::Client wc(namPtr, ssoPtr); EXPECT_CALL(sso, getCredentials()).WillOnce(Invoke([&]() { sso.credentialsNotFound(); })); EXPECT_CALL(nam, sendCustomRequest(_, _, _)).Times(0); auto wr = wc.call(FAKE_SERVER + FAKE_PATH, "HEAD", true); } TEST_F(WebClientTest, testResponseFinished) { using namespace ::testing; auto reply = new NiceMock(); ON_CALL(*reply, readAll()).WillByDefault(Return("HOLA")); QSharedPointer replyPtr(reply); click::web::Client wc(namPtr, ssoPtr); EXPECT_CALL(nam, sendCustomRequest(_, _, _)) .Times(1) .WillOnce(Return(replyPtr)); auto response = wc.call(FAKE_SERVER + FAKE_PATH); QObject::connect(response.data(), &click::web::Response::finished, [this](){replyHandler();}); EXPECT_CALL(*this, replyHandler()); emit reply->finished(); } TEST_F(WebClientTest, testResponseFailed) { using namespace ::testing; auto reply = new NiceMock(); QSharedPointer replyPtr(reply); click::web::Client wc(namPtr, ssoPtr); EXPECT_CALL(nam, sendCustomRequest(_, _, _)) .Times(1) .WillOnce(Return(replyPtr)); EXPECT_CALL(*reply, errorString()).Times(1).WillOnce(Return("fake error")); auto response = wc.call(FAKE_SERVER + FAKE_PATH); QObject::connect(response.data(), &click::web::Response::error, [this, &response](QString desc){ errorHandler(desc); Q_UNUSED(response); }); EXPECT_CALL(*this, errorHandler(_)); emit reply->error(QNetworkReply::UnknownNetworkError); } TEST_F(WebClientTest, testResponseAbort) { using namespace ::testing; auto reply = new NiceMock(); QSharedPointer replyPtr(reply); click::web::Client wc(namPtr, ssoPtr); EXPECT_CALL(nam, sendCustomRequest(_, _, _)) .Times(1) .WillOnce(Return(replyPtr)); auto response = wc.call(FAKE_SERVER + FAKE_PATH); EXPECT_CALL(*reply, abort()).Times(1); response->abort(); } unity-scope-click-0.1+14.04.20140417/scope/tests/CMakeLists.txt0000644000015301777760000000350612323764232024337 0ustar pbusernogroup00000000000000set (CLICKSCOPE_TESTS_TARGET click-scope-tests) find_package(Threads) # Build with system gmock and embedded gtest set (GMOCK_INCLUDE_DIR "/usr/include/gmock/include" CACHE PATH "gmock source include directory") set (GMOCK_SOURCE_DIR "/usr/src/gmock" CACHE PATH "gmock source directory") set (GTEST_INCLUDE_DIR "${GMOCK_SOURCE_DIR}/gtest/include" CACHE PATH "gtest source include directory") add_subdirectory(${GMOCK_SOURCE_DIR} "${CMAKE_CURRENT_BINARY_DIR}/gmock") # Qt5 bits SET (CMAKE_INCLUDE_CURRENT_DIR ON) SET (CMAKE_AUTOMOC ON) find_package(Qt5Core REQUIRED) include_directories ( ${CMAKE_SOURCE_DIR}/scope ${JSON_CPP_INCLUDE_DIRS} ${GTEST_INCLUDE_DIR} ${GMOCK_INCLUDE_DIR} ) configure_file(${CMAKE_CURRENT_SOURCE_DIR}/test_data.cpp.in ${CMAKE_CURRENT_BINARY_DIR}/test_data.cpp) add_executable (${CLICKSCOPE_TESTS_TARGET} mock_network_access_manager.h mock_webclient.h test_download_manager.cpp test_index.cpp test_interface.cpp test_reviews.cpp test_smartconnect.cpp test_webclient.cpp ${CMAKE_CURRENT_BINARY_DIR}/test_data.cpp ) qt5_use_modules(${CLICKSCOPE_TESTS_TARGET} Core DBus Network Test) target_link_libraries(${CLICKSCOPE_TESTS_TARGET} ${SCOPE_LIB_UNVERSIONED} ${UNITY_SCOPES_LDFLAGS} ${UBUNTUONE_LDFLAGS} ${UBUNTU_DOWNLOAD_MANAGER_CLIENT_LDFLAGS} ${UBUNTU_DOWNLOAD_MANAGER_COMMON_LDFLAGS} ${JSON_CPP_LDFLAGS} gmock gmock_main ${CMAKE_THREAD_LIBS_INIT} ) add_custom_target (test-click-scope COMMAND ${CMAKE_CURRENT_BINARY_DIR}/${CLICKSCOPE_TESTS_TARGET} DEPENDS ${CLICKSCOPE_TESTS_TARGET} ) add_custom_target (test-click-scope-disabled COMMAND GTEST_ALSO_RUN_DISABLED_TESTS=1 ${CMAKE_CURRENT_BINARY_DIR}/${CLICKSCOPE_TESTS_TARGET} DEPENDS ${CLICKSCOPE_TESTS_TARGET} ) add_subdirectory(integration) add_subdirectory(download_manager_tool) add_subdirectory(click_interface_tool)unity-scope-click-0.1+14.04.20140417/scope/tests/test_runner.h0000644000015301777760000000644512323764232024325 0ustar pbusernogroup00000000000000/* * Copyright (C) 2014 Canonical Ltd. * * This program is free software: you can redistribute it and/or modify it * under the terms of the GNU General Public License version 3, as published * by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranties of * MERCHANTABILITY, SATISFACTORY QUALITY, or FITNESS FOR A PARTICULAR * PURPOSE. See the GNU General Public License for more details. * * You should have received a copy of the GNU General Public License along * with this program. If not, see . * * In addition, as a special exception, the copyright holders give * permission to link the code of portions of this program with the * OpenSSL library under certain conditions as described in each * individual source file, and distribute linked combinations * including the two. * You must obey the GNU General Public License in all respects * for all of the code used other than OpenSSL. If you modify * file(s) with this exception, you may extend this exception to your * version of the file(s), but you are not obligated to do so. If you * do not wish to do so, delete this exception statement from your * version. If you delete this exception statement from all source * files in the program, then also delete it here. */ #ifndef TEST_RUNNER_H #define TEST_RUNNER_H #include #include #include #include #include class TestRunner { public: static TestRunner& Instance() { static TestRunner instance; return instance; } template char RegisterTest(char* name) { if(!_tests.contains(name)) { QSharedPointer test(new T()); _tests.insert(name, QSharedPointer(test)); } return char(1); } int RunAll(int argc, char *argv[]) { // provide command line to run a single test case QCoreApplication* app = QCoreApplication::instance(); QStringList args = app->arguments(); if (args.contains("-testcase")) { int index = args.indexOf("-testcase"); if (args.count() > index + 1) { QString testcase = args[index + 1]; if (_tests.contains(testcase)) { args.removeAt(index + 1); args.removeAt(index); return QTest::qExec(_tests[testcase].data(), args); } else { return -1; } } else { return -1; } } else { int errorCode = 0; foreach (QString const &testName, _tests.keys()) { errorCode |= QTest::qExec(_tests[testName].data(), argc, argv); std::cout << std::endl; } return errorCode; } } private: QMap > _tests; }; // Use this macro after your test declaration #define DECLARE_TEST(className)\ static char test_##className = TestRunner::Instance().RegisterTest(const_cast(#className)); // Use this macro to execute all tests #define RUN_ALL_QTESTS(argc, argv)\ TestRunner::Instance().RunAll(argc, argv); #endif // TEST_RUNNER_H unity-scope-click-0.1+14.04.20140417/scope/tests/mock_network_access_manager.h0000644000015301777760000000600412323764232027461 0ustar pbusernogroup00000000000000/* * Copyright (C) 2014 Canonical Ltd. * * This program is free software: you can redistribute it and/or modify it * under the terms of the GNU General Public License version 3, as published * by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranties of * MERCHANTABILITY, SATISFACTORY QUALITY, or FITNESS FOR A PARTICULAR * PURPOSE. See the GNU General Public License for more details. * * You should have received a copy of the GNU General Public License along * with this program. If not, see . * * In addition, as a special exception, the copyright holders give * permission to link the code of portions of this program with the * OpenSSL library under certain conditions as described in each * individual source file, and distribute linked combinations * including the two. * You must obey the GNU General Public License in all respects * for all of the code used other than OpenSSL. If you modify * file(s) with this exception, you may extend this exception to your * version of the file(s), but you are not obligated to do so. If you * do not wish to do so, delete this exception statement from your * version. If you delete this exception statement from all source * files in the program, then also delete it here. */ #ifndef MOCK_NETWORK_ACCESS_MANAGER_H #define MOCK_NETWORK_ACCESS_MANAGER_H #include #include #include #include class MockNetworkReply : public click::network::Reply { public slots: void sendFinished(); void sendError(); public: MOCK_METHOD0(abort, void()); MOCK_METHOD0(readAll, QByteArray()); MOCK_METHOD1(attribute, QVariant(QNetworkRequest::Attribute)); MOCK_METHOD1(hasRawHeader, bool(const QByteArray&)); MOCK_METHOD1(rawHeader, QString(const QByteArray &headerName)); // We have to typedef the result here as the preprocessor is dumb // and would interpret the "," in the template spec as part of the // macro declaration and not part of the signature. typedef QList> ResultType; MOCK_METHOD0(rawHeaderPairs, ResultType()); MOCK_METHOD0(errorString, QString()); }; struct MockNetworkAccessManager : public click::network::AccessManager { MockNetworkAccessManager() { } MOCK_METHOD1(get, QSharedPointer(QNetworkRequest&)); MOCK_METHOD1(head, QSharedPointer(QNetworkRequest&)); MOCK_METHOD2(post, QSharedPointer(QNetworkRequest&, QByteArray&)); MOCK_METHOD3(sendCustomRequest, QSharedPointer(QNetworkRequest&, QByteArray&, QIODevice*)); static QList scripted_responses; static QList performed_get_requests; static QList performed_head_requests; static bool shouldSignalNetworkError; }; #endif // MOCK_NETWORK_ACCESS_MANAGER_H unity-scope-click-0.1+14.04.20140417/scope/click/0000755000015301777760000000000012323764741021523 5ustar pbusernogroup00000000000000unity-scope-click-0.1+14.04.20140417/scope/click/query.cpp0000644000015301777760000002702012323764232023370 0ustar pbusernogroup00000000000000/* * Copyright (C) 2014 Canonical Ltd. * * This program is free software: you can redistribute it and/or modify it * under the terms of the GNU General Public License version 3, as published * by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranties of * MERCHANTABILITY, SATISFACTORY QUALITY, or FITNESS FOR A PARTICULAR * PURPOSE. See the GNU General Public License for more details. * * You should have received a copy of the GNU General Public License along * with this program. If not, see . * * In addition, as a special exception, the copyright holders give * permission to link the code of portions of this program with the * OpenSSL library under certain conditions as described in each * individual source file, and distribute linked combinations * including the two. * You must obey the GNU General Public License in all respects * for all of the code used other than OpenSSL. If you modify * file(s) with this exception, you may extend this exception to your * version of the file(s), but you are not obligated to do so. If you * do not wish to do so, delete this exception statement from your * version. If you delete this exception statement from all source * files in the program, then also delete it here. */ #include "query.h" #include "qtbridge.h" #include "key_file_locator.h" #include "interface.h" #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "click-i18n.h" namespace { std::string CATEGORY_APPS_DISPLAY = R"( { "schema-version" : 1, "template" : { "category-layout" : "grid", "card-size": "small" }, "components" : { "title" : "title", "art" : { "field": "art", "aspect-ratio": 1.6, "fill-mode": "fit" } } } )"; std::string CATEGORY_APPS_SEARCH = R"( { "schema-version" : 1, "template" : { "category-layout" : "grid", "card-layout" : "horizontal", "card-size": "large" }, "components" : { "title" : "title", "mascot" : { "field": "art" }, "subtitle": "publisher" } } )"; QNetworkAccessManager* getNetworkAccessManager(qt::core::world::Environment& env) { static qt::HeapAllocatedObject nam = env.allocate(&env); return env.resolve(nam); } QString architectureFromDpkg() { QString program("dpkg"); QStringList arguments; arguments << "--print-architecture"; QProcess archDetector; archDetector.start(program, arguments); if(!archDetector.waitForFinished()) { throw std::runtime_error("Architecture detection failed."); } auto output = archDetector.readAllStandardOutput(); auto ostr = QString::fromUtf8(output); ostr.remove('\n'); return ostr; } const QString& architecture() { static const QString arch{architectureFromDpkg()}; return arch; } class ReplyWrapper : public QObject { Q_OBJECT public: ReplyWrapper(QNetworkReply* reply, const scopes::SearchReplyProxy& replyProxy, const std::set& installedApplications, const std::string& categoryTemplate, const QString& queryUri, QObject* parent) : QObject(parent), reply(reply), replyProxy(replyProxy), installedApplications(installedApplications), renderer(categoryTemplate), category(replyProxy->register_category("appstore", _("Available"), "", renderer)), queryUrl(queryUri) { } void cancel() { reply->abort(); } public slots: void downloadFinished(QNetworkReply *reply) { // If the user types a search term, multiple queries are kicked off // and we have to make sure that we only consider results from the query // that this reply corresponds to. if (reply->url() != queryUrl) return; // category might be null if the query got cancelled before the ReplyWrapper got created // unlikely, but still possible. if (!category) return; struct Scope { Scope(QNetworkReply* reply, ReplyWrapper* wrapper) : reply(reply), wrapper(wrapper) { } ~Scope() { reply->deleteLater(); wrapper->deleteLater(); } QNetworkReply* reply; ReplyWrapper* wrapper; } scope(reply, this); if (reply->error() != QNetworkReply::NoError) { std::cerr << __PRETTY_FUNCTION__ << ": Received network reply with error: " << reply->errorString().toStdString() << std::endl; return; } try { QByteArray msg = reply->readAll(); QJsonParseError jsonParseError; QJsonDocument doc = QJsonDocument::fromJson(msg, &jsonParseError); if (jsonParseError.error != QJsonParseError::NoError) throw std::runtime_error( "ReplyWrapper::onDownloadFinished: Cannot parse JSON from server response with " + jsonParseError.errorString().toStdString()); QJsonArray results = doc.array(); for(const auto &entry : results) { if(not entry.isObject()) { // FIXME: write message about invalid JSON here. continue; } scopes::CategorisedResult res(category); QJsonObject obj = entry.toObject(); std::string resourceUrl = obj[click::Query::JsonKeys::RESOURCE_URL].toString().toUtf8().data(); std::string title = obj[click::Query::JsonKeys::TITLE].toString().toUtf8().data(); std::string iconUrl = obj[click::Query::JsonKeys::ICON_URL].toString().toUtf8().data(); std::string name = obj[click::Query::JsonKeys::NAME].toString().toUtf8().data(); if (installedApplications.count(name) > 0) continue; res.set_uri(queryUrl.toString().toUtf8().data()); res.set_title(title); res.set_art(iconUrl); res.set_dnd_uri(queryUrl.toString().toUtf8().data()); res[click::Query::ResultKeys::NAME] = name; res[click::Query::ResultKeys::INSTALLED] = false; res[click::Query::ResultKeys::DOWNLOAD_URL] = resourceUrl; // FIXME at this point we should go through the rest of the fields // and convert them. replyProxy->push(res); } } catch(const std::exception& e) { std::cerr << __PRETTY_FUNCTION__ << ": Caught std::exception with: " << e.what() << std::endl; } catch(...) { std::cerr << __PRETTY_FUNCTION__ << ": Caught exception" << std::endl; } } private: QNetworkReply* reply; scopes::SearchReplyProxy replyProxy; std::set installedApplications; scopes::CategoryRenderer renderer; scopes::Category::SCPtr category; QUrl queryUrl; }; } static void push_local_results(scopes::SearchReplyProxy const &replyProxy, std::vector const &apps, std::string categoryTemplate) { scopes::CategoryRenderer rdr(categoryTemplate); auto cat = replyProxy->register_category("local", _("My apps"), "", rdr); // cat might be null when the underlying query got cancelled. if (!cat) return; for(const auto & a: apps) { scopes::CategorisedResult res(cat); res.set_title(a.title); res.set_art(a.icon_url); res.set_uri(a.url); res[click::Query::ResultKeys::NAME] = a.name; res[click::Query::ResultKeys::DESCRIPTION] = a.description; res[click::Query::ResultKeys::MAIN_SCREENSHOT] = a.main_screenshot; res[click::Query::ResultKeys::INSTALLED] = true; res[click::Query::ResultKeys::VERSION] = a.version; replyProxy->push(res); } } struct click::Query::Private { Private(const std::string& query, const scopes::SearchMetadata& metadata) : query(query), meta(metadata) { } std::string query; scopes::SearchMetadata meta; qt::HeapAllocatedObject replyWrapper; }; click::Query::Query(std::string const& query, scopes::SearchMetadata const& metadata) : impl(new Private(query, metadata)) { } click::Query::~Query() { } void click::Query::cancelled() { qt::core::world::enter_with_task([=](qt::core::world::Environment& env) { if (impl->replyWrapper) env.resolve(impl->replyWrapper)->cancel(); }); } namespace { click::Interface& clickInterfaceInstance() { static QSharedPointer keyFileLocator(new click::KeyFileLocator()); static click::Interface iface(keyFileLocator); return iface; } QString frameworks_arg() { std::stringstream frameworks; for (auto f: click::FrameworkLocator().get_available_frameworks()) { frameworks << ",framework:" << f; } return QString::fromStdString(frameworks.str()); } } void click::Query::run(scopes::SearchReplyProxy const& searchReply) { QString query = QString::fromStdString(impl->query); std::string categoryTemplate = CATEGORY_APPS_SEARCH; if (query.isEmpty()) { categoryTemplate = CATEGORY_APPS_DISPLAY; } auto localResults = clickInterfaceInstance().find_installed_apps( query); push_local_results( searchReply, localResults, categoryTemplate); std::set locallyInstalledApps; for(const auto& app : localResults) locallyInstalledApps.insert(app.name); static const std::string no_net_hint("no-internet"); if (impl->meta.contains_hint(no_net_hint)) { auto var = impl->meta[no_net_hint]; if (var.which() == scopes::Variant::Type::Bool && var.get_bool()) { return; } } qt::core::world::enter_with_task([=](qt::core::world::Environment& env) { static const QString queryPattern( "https://search.apps.ubuntu.com/api/v1/search?q=%1" "%2,architecture:%3"); QString queryUri = queryPattern.arg(QString::fromUtf8(impl->query.c_str())) .arg(frameworks_arg()).arg(architecture()); auto nam = getNetworkAccessManager(env); auto networkReply = nam->get(QNetworkRequest(QUrl(queryUri))); impl->replyWrapper = env.allocate(networkReply, searchReply, locallyInstalledApps, categoryTemplate, queryUri, &env); auto rw = env.resolve(impl->replyWrapper); QObject::connect( nam, &QNetworkAccessManager::finished, rw, &ReplyWrapper::downloadFinished); }); } #include "query.moc" unity-scope-click-0.1+14.04.20140417/scope/click/ubuntuone_credentials.cpp0000644000015301777760000000426712323764232026634 0ustar pbusernogroup00000000000000/* * Copyright (C) 2014 Canonical Ltd. * * This program is free software: you can redistribute it and/or modify it * under the terms of the GNU General Public License version 3, as published * by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranties of * MERCHANTABILITY, SATISFACTORY QUALITY, or FITNESS FOR A PARTICULAR * PURPOSE. See the GNU General Public License for more details. * * You should have received a copy of the GNU General Public License along * with this program. If not, see . * * In addition, as a special exception, the copyright holders give * permission to link the code of portions of this program with the * OpenSSL library under certain conditions as described in each * individual source file, and distribute linked combinations * including the two. * You must obey the GNU General Public License in all respects * for all of the code used other than OpenSSL. If you modify * file(s) with this exception, you may extend this exception to your * version of the file(s), but you are not obligated to do so. If you * do not wish to do so, delete this exception statement from your * version. If you delete this exception statement from all source * files in the program, then also delete it here. */ #include "ubuntuone_credentials.h" namespace u1 = UbuntuOne; click::CredentialsService::CredentialsService() : ssoService(new u1::SSOService()) { // Forward signals directly: connect(ssoService.data(), &u1::SSOService::credentialsFound, this, &click::CredentialsService::credentialsFound); connect(ssoService.data(), &u1::SSOService::credentialsNotFound, this, &click::CredentialsService::credentialsNotFound); connect(ssoService.data(), &u1::SSOService::credentialsDeleted, this, &click::CredentialsService::credentialsDeleted); } click::CredentialsService::~CredentialsService() { } void click::CredentialsService::getCredentials() { ssoService->getCredentials(); } void click::CredentialsService::invalidateCredentials() { ssoService->invalidateCredentials(); } unity-scope-click-0.1+14.04.20140417/scope/click/reviews.cpp0000644000015301777760000001202712323764232023710 0ustar pbusernogroup00000000000000/* * Copyright (C) 2014 Canonical Ltd. * * This program is free software: you can redistribute it and/or modify it * under the terms of the GNU General Public License version 3, as published * by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranties of * MERCHANTABILITY, SATISFACTORY QUALITY, or FITNESS FOR A PARTICULAR * PURPOSE. See the GNU General Public License for more details. * * You should have received a copy of the GNU General Public License along * with this program. If not, see . * * In addition, as a special exception, the copyright holders give * permission to link the code of portions of this program with the * OpenSSL library under certain conditions as described in each * individual source file, and distribute linked combinations * including the two. * You must obey the GNU General Public License in all respects * for all of the code used other than OpenSSL. If you modify * file(s) with this exception, you may extend this exception to your * version of the file(s), but you are not obligated to do so. If you * do not wish to do so, delete this exception statement from your * version. If you delete this exception statement from all source * files in the program, then also delete it here. */ #include #include #include #include #include "reviews.h" namespace click { bool operator== (const Review& lhs, const Review &rhs) { return lhs.id == rhs.id && lhs.rating == rhs.rating && lhs.usefulness_favorable == rhs.usefulness_favorable && lhs.usefulness_total == rhs.usefulness_total && lhs.hide == rhs.hide && lhs.date_created == rhs.date_created && lhs.date_deleted == rhs.date_deleted && lhs.package_name == rhs.package_name && lhs.package_version == rhs.package_version && lhs.language == rhs.language && lhs.summary == rhs.summary && lhs.review_text == rhs.review_text && lhs.reviewer_name == rhs.reviewer_name && lhs.reviewer_username == rhs.reviewer_username; } ReviewList review_list_from_json (const std::string& json) { std::istringstream stream(json); boost::property_tree::ptree tree; boost::property_tree::read_json(stream, tree); ReviewList reviews; BOOST_FOREACH(boost::property_tree::ptree::value_type &value, tree) { assert(value.first.empty()); // array elements have no names auto node = value.second; Review review; review.id = node.get("id"); review.rating = node.get("rating"); review.usefulness_favorable = node.get("usefulness_favorable"); review.usefulness_total = node.get("usefulness_total"); review.hide = node.get("hide"); review.date_created = node.get("date_created"); review.date_deleted = node.get("date_deleted"); review.package_name = node.get("package_name"); review.package_version = node.get("version"); review.language = node.get("language"); review.summary = node.get("summary"); review.review_text = node.get("review_text"); review.reviewer_username = node.get("reviewer_username"); review.reviewer_name = node.get("reviewer_displayname", review.reviewer_username); reviews.push_back(review); } return reviews; } Reviews::Reviews (const QSharedPointer& client) : client(client) { } Reviews::~Reviews () { } click::web::Cancellable Reviews::fetch_reviews (const std::string& package_name, std::function callback) { click::web::CallParams params; params.add(click::REVIEWS_QUERY_ARGNAME, package_name.c_str()); QSharedPointer response = client->call (get_base_url() + click::REVIEWS_API_PATH, params); QObject::connect(response.data(), &click::web::Response::finished, [=](QString reply) { click::ReviewList reviews; reviews = review_list_from_json(reply.toUtf8().constData()); callback(reviews, click::Reviews::Error::NoError); }); QObject::connect(response.data(), &click::web::Response::error, [=](QString) { qDebug() << "Network error attempting to fetch reviews for:" << package_name.c_str(); callback(ReviewList(), click::Reviews::Error::NetworkError); }); return click::web::Cancellable(response); } std::string Reviews::get_base_url () { const char *env_url = getenv(REVIEWS_BASE_URL_ENVVAR.c_str()); if (env_url != NULL) { return env_url;; } return click::REVIEWS_BASE_URL; } } //namespace click unity-scope-click-0.1+14.04.20140417/scope/click/application.h0000644000015301777760000000433312323764232024175 0ustar pbusernogroup00000000000000/* * Copyright (C) 2014 Canonical Ltd. * * This program is free software: you can redistribute it and/or modify it * under the terms of the GNU General Public License version 3, as published * by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranties of * MERCHANTABILITY, SATISFACTORY QUALITY, or FITNESS FOR A PARTICULAR * PURPOSE. See the GNU General Public License for more details. * * You should have received a copy of the GNU General Public License along * with this program. If not, see . * * In addition, as a special exception, the copyright holders give * permission to link the code of portions of this program with the * OpenSSL library under certain conditions as described in each * individual source file, and distribute linked combinations * including the two. * You must obey the GNU General Public License in all respects * for all of the code used other than OpenSSL. If you modify * file(s) with this exception, you may extend this exception to your * version of the file(s), but you are not obligated to do so. If you * do not wish to do so, delete this exception statement from your * version. If you delete this exception statement from all source * files in the program, then also delete it here. */ #ifndef CLICK_APPLICATION_H #define CLICK_APPLICATION_H #include "index.h" namespace click { struct Application : public Package { Application(std::string name, std::string title, double price, std::string icon_url, std::string url, std::string description, std::string main_screenshot ) : Package {name, title, price, icon_url, url}, description(description), main_screenshot(main_screenshot) { } Application() = default; ~Application() {} std::string description; std::string main_screenshot; }; std::ostream& operator<<(std::ostream& out, const Application& app); bool operator==(const Application& lhs, const Application& rhs); } // namespace click #endif // CLICK_APPLICATION_H unity-scope-click-0.1+14.04.20140417/scope/click/qtbridge.cpp0000644000015301777760000001116312323764232024025 0ustar pbusernogroup00000000000000/* * Copyright © 2014 Canonical Ltd. * * This program is free software: you can redistribute it and/or modify it * under the terms of the GNU Lesser General Public License version 3, * as published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public License * along with this program. If not, see . * * Authored by: Jussi Pakkanen * Thomas Voß */ #include "qtbridge.h" #include #include #include #include #include #include #include namespace { QCoreApplication* app = nullptr; } namespace qt { namespace core { namespace world { Environment::Environment(QObject *parent) : QObject(parent) { } namespace detail { QEvent::Type qt_core_world_task_event_type() { static QEvent::Type event_type = static_cast(QEvent::registerEventType()); return event_type; } class Environment : public qt::core::world::Environment { public: Environment(QObject* parent) : qt::core::world::Environment(parent) { } }; class TaskEvent : public QEvent { public: TaskEvent(const std::function& task) : QEvent(qt_core_world_task_event_type()), task(task) { } void run(qt::core::world::Environment& env) { try { task(env); promise.set_value(); } catch(...) { promise.set_exception(std::current_exception()); } } std::future get_future() { return promise.get_future(); } private: std::function task; std::promise promise; }; class TaskHandler : public QObject { Q_OBJECT public: TaskHandler(QObject* parent) : QObject(parent) { } bool event(QEvent* e); }; void createCoreApplicationInstanceWithArgs(int argc, char** argv) { app = new QCoreApplication(argc, argv); } void destroyCoreApplicationInstace() { delete app; } QCoreApplication* coreApplicationInstance() { return app; } TaskHandler* task_handler() { static TaskHandler* instance = new TaskHandler(coreApplicationInstance()); return instance; } qt::core::world::Environment* environment() { static detail::Environment* env = new detail::Environment(coreApplicationInstance()); return env; } bool TaskHandler::event(QEvent *e) { if (e->type() != qt_core_world_task_event_type()) return QObject::event(e); auto te = dynamic_cast(e); if (te) { te->run(*environment()); return true; } return false; } } void build_and_run(int argc, char** argv, const std::function& ready) { QThread::currentThread(); if (QCoreApplication::instance() != nullptr) throw std::runtime_error( "qt::core::world::build_and_run: " "There is already a QCoreApplication running."); detail::createCoreApplicationInstanceWithArgs(argc, argv); detail::task_handler()->moveToThread( detail::coreApplicationInstance()->thread()); detail::environment()->moveToThread( detail::coreApplicationInstance()->thread()); // Signal to other worlds that we are good to go. ready(); detail::coreApplicationInstance()->exec(); // Someone has called quit and we clean up on the correct thread here. detail::destroyCoreApplicationInstace(); } void destroy() { enter_with_task([](qt::core::world::Environment&) { // We make sure that all tasks have completed before quitting the app. QEventLoopLocker locker; }).wait_for(std::chrono::seconds{1}); } std::future enter_with_task(const std::function& task) { QCoreApplication* instance = QCoreApplication::instance(); if (!instance) { throw std::runtime_error("Qt world has not been built before calling this function."); } detail::TaskEvent* te = new detail::TaskEvent(task); auto future = te->get_future(); // We hand over ownership of te here. The event is deleted later after it has // been processed by the event loop. instance->postEvent(detail::task_handler(), te); return future; } } } } #include "qtbridge.moc" unity-scope-click-0.1+14.04.20140417/scope/click/smartconnect.cpp0000644000015301777760000000333312323764232024724 0ustar pbusernogroup00000000000000/* * Copyright (C) 2014 Canonical Ltd. * * This program is free software: you can redistribute it and/or modify it * under the terms of the GNU General Public License version 3, as published * by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranties of * MERCHANTABILITY, SATISFACTORY QUALITY, or FITNESS FOR A PARTICULAR * PURPOSE. See the GNU General Public License for more details. * * You should have received a copy of the GNU General Public License along * with this program. If not, see . * * In addition, as a special exception, the copyright holders give * permission to link the code of portions of this program with the * OpenSSL library under certain conditions as described in each * individual source file, and distribute linked combinations * including the two. * You must obey the GNU General Public License in all respects * for all of the code used other than OpenSSL. If you modify * file(s) with this exception, you may extend this exception to your * version of the file(s), but you are not obligated to do so. If you * do not wish to do so, delete this exception statement from your * version. If you delete this exception statement from all source * files in the program, then also delete it here. */ #include "smartconnect.h" namespace click { namespace utils { SmartConnect::SmartConnect(QObject *parent) : QObject(parent) { } void SmartConnect::cleanup() { deleteLater(); } void SmartConnect::disconnectAll() { foreach (auto c, connections) { QObject::disconnect(c); } cleanup(); } } // namespace utils } // namespace click unity-scope-click-0.1+14.04.20140417/scope/click/preview.cpp0000644000015301777760000004423212323764232023710 0ustar pbusernogroup00000000000000/* * Copyright (C) 2014 Canonical Ltd. * * This program is free software: you can redistribute it and/or modify it * under the terms of the GNU General Public License version 3, as published * by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranties of * MERCHANTABILITY, SATISFACTORY QUALITY, or FITNESS FOR A PARTICULAR * PURPOSE. See the GNU General Public License for more details. * * You should have received a copy of the GNU General Public License along * with this program. If not, see . * * In addition, as a special exception, the copyright holders give * permission to link the code of portions of this program with the * OpenSSL library under certain conditions as described in each * individual source file, and distribute linked combinations * including the two. * You must obey the GNU General Public License in all respects * for all of the code used other than OpenSSL. If you modify * file(s) with this exception, you may extend this exception to your * version of the file(s), but you are not obligated to do so. If you * do not wish to do so, delete this exception statement from your * version. If you delete this exception statement from all source * files in the program, then also delete it here. */ #include "preview.h" #include "qtbridge.h" #include "download-manager.h" #include #include #include #include #include #include #include #include #include "click-i18n.h" namespace click { // Preview base class Preview::Preview(const unity::scopes::Result& result) : result(result) { } Preview::Preview(const unity::scopes::Result& result, const QSharedPointer& client) : result(result), index(new click::Index(client)), reviews(new click::Reviews(client)) { } Preview::~Preview() { } void Preview::cancelled() { index_operation.cancel(); reviews_operation.cancel(); } // TODO: error handling - once get_details provides errors, we can // return them from populateDetails and check them in the calling code // to decide whether to show error widgets. see bug LP: #1289541 void Preview::populateDetails(std::function details_callback, std::function reviews_callback) { std::string app_name = result["name"].get_string(); if (app_name.empty()) { click::PackageDetails details; qDebug() << "in populateDetails(), app_name is empty"; details.package.title = result.title(); details.package.icon_url = result.art(); details.description = result["description"].get_string(); details.main_screenshot_url = result["main_screenshot"].get_string(); details_callback(details); reviews_callback(click::ReviewList(), click::Reviews::Error::NoError); } else { qDebug() << "in populateDetails(), app_name is:" << app_name.c_str(); // I think this should not be required when we switch the click::Index over // to using the Qt bridge. With that, the qt dependency becomes an implementation detail // and code using it does not need to worry about threading/event loop topics. qt::core::world::enter_with_task([this, details_callback, reviews_callback, app_name](qt::core::world::Environment&) { index_operation = index->get_details(app_name, [this, app_name, details_callback, reviews_callback](PackageDetails details, click::Index::Error error){ if(error == click::Index::Error::NoError) { qDebug() << "Got details:" << app_name.c_str(); details_callback(details); } else { qDebug() << "Error getting details for:" << app_name.c_str(); // TODO: handle error getting details } reviews_operation = reviews->fetch_reviews(app_name, reviews_callback); }); }); } } scopes::PreviewWidgetList Preview::headerWidgets(const click::PackageDetails& details) { scopes::PreviewWidgetList widgets; bool has_screenshots = !details.main_screenshot_url.empty() || !details.more_screenshots_urls.empty(); if (has_screenshots) { scopes::PreviewWidget gallery("screenshots", "gallery"); scopes::VariantArray arr; if (!details.main_screenshot_url.empty()) arr.push_back(scopes::Variant(details.main_screenshot_url)); if (!details.more_screenshots_urls.empty()) { for (auto const& s: details.more_screenshots_urls) { arr.push_back(scopes::Variant(s)); } } gallery.add_attribute_value("sources", scopes::Variant(arr)); widgets.push_back(gallery); } scopes::PreviewWidget header("hdr", "header"); header.add_attribute_value("title", scopes::Variant(details.package.title)); if (!details.description.empty()) { std::stringstream ss(details.description); std::string first_line; if (std::getline(ss, first_line)) header.add_attribute_value("subtitle", scopes::Variant(first_line)); } if (!details.package.icon_url.empty()) header.add_attribute_value("mascot", scopes::Variant(details.package.icon_url)); widgets.push_back(header); return widgets; } scopes::PreviewWidgetList Preview::descriptionWidgets(const click::PackageDetails& details) { scopes::PreviewWidgetList widgets; if (details.description.empty()) { return widgets; } scopes::PreviewWidget summary("summary", "text"); summary.add_attribute_value("text", scopes::Variant(details.description)); widgets.push_back(summary); return widgets; } scopes::PreviewWidgetList Preview::reviewsWidgets(const click::ReviewList& reviewlist) { scopes::PreviewWidgetList widgets; scopes::PreviewWidget rating("summary", "reviews"); scopes::VariantBuilder builder; if (reviewlist.size() > 0) { for (const auto& kv : reviewlist) { builder.add_tuple({ {"rating", scopes::Variant(kv.rating)}, {"author", scopes::Variant(kv.reviewer_name)}, {"review", scopes::Variant(kv.review_text)} }); } rating.add_attribute_value("reviews", builder.end()); widgets.push_back(rating); } return widgets; } scopes::PreviewWidgetList Preview::downloadErrorWidgets() { return errorWidgets(scopes::Variant(_("Download Error")), scopes::Variant(_("Download or install failed. Please try again.")), scopes::Variant(click::Preview::Actions::CLOSE_PREVIEW), // TODO see bug LP: #1289434 scopes::Variant(_("Close"))); } scopes::PreviewWidgetList Preview::loginErrorWidgets() { return errorWidgets(scopes::Variant(_("Login Error")), scopes::Variant(_("Please log in to your Ubuntu One account.")), scopes::Variant(click::Preview::Actions::OPEN_ACCOUNTS), scopes::Variant(_("Go to Accounts"))); } scopes::PreviewWidgetList Preview::errorWidgets(const scopes::Variant& title, const scopes::Variant& subtitle, const scopes::Variant& action_id, const scopes::Variant& action_label) { scopes::PreviewWidgetList widgets; scopes::PreviewWidget header("hdr", "header"); header.add_attribute_value("title", title); header.add_attribute_value("subtitle", subtitle); widgets.push_back(header); scopes::PreviewWidget buttons("buttons", "actions"); scopes::VariantBuilder builder; builder.add_tuple({ {"id", action_id}, {"label", action_label} }); buttons.add_attribute_value("actions", builder.end()); widgets.push_back(buttons); return widgets; } // class DownloadErrorPreview DownloadErrorPreview::DownloadErrorPreview(const unity::scopes::Result &result) : Preview(result) { } DownloadErrorPreview::~DownloadErrorPreview() { } void DownloadErrorPreview::run(const unity::scopes::PreviewReplyProxy &reply) { // NOTE: no details used by downloadErrorWidgets(), so no need to // call populateDetails() here. reply->push(downloadErrorWidgets()); } // class InstallingPreview InstallingPreview::InstallingPreview(const std::string &download_url, const unity::scopes::Result &result, const QSharedPointer& client, const QSharedPointer &nam) : Preview(result, client), download_url(download_url), downloader(new click::Downloader(nam)) { } InstallingPreview::~InstallingPreview() { } void InstallingPreview::run(const unity::scopes::PreviewReplyProxy &reply) { downloader->startDownload(download_url, result["name"].get_string(), [this, reply] (std::pair rc){ // NOTE: details not needed by fooErrorWidgets, so no need to populateDetails(): switch (rc.second) { case InstallError::CredentialsError: qWarning() << "InstallingPreview got error in getting credentials during startDownload"; reply->push(loginErrorWidgets()); return; case InstallError::DownloadInstallError: qWarning() << "Error received from UDM during startDownload:" << rc.first.c_str(); reply->push(downloadErrorWidgets()); return; default: qDebug() << "Successfully created UDM Download."; populateDetails([this, reply, rc](const PackageDetails &details){ reply->push(headerWidgets(details)); reply->push(progressBarWidget(rc.first)); reply->push(descriptionWidgets(details)); }, [this, reply](const ReviewList& reviewlist, click::Reviews::Error error) { if (error == click::Reviews::Error::NoError) { reply->push(reviewsWidgets(reviewlist)); } else { qDebug() << "There was an error getting reviews for:" << result["name"].get_string().c_str(); } reply->finished(); }); break; } }); } scopes::PreviewWidgetList InstallingPreview::progressBarWidget(const std::string& object_path) { scopes::PreviewWidgetList widgets; scopes::PreviewWidget progress("download", "progress"); scopes::VariantMap tuple; tuple["dbus-name"] = "com.canonical.applications.Downloader"; tuple["dbus-object"] = object_path; progress.add_attribute_value("source", scopes::Variant(tuple)); widgets.push_back(progress); return widgets; } // class InstalledPreview InstalledPreview::InstalledPreview(const unity::scopes::Result& result, const QSharedPointer& client) : Preview(result, client) { } InstalledPreview::~InstalledPreview() { } void InstalledPreview::run(unity::scopes::PreviewReplyProxy const& reply) { populateDetails([this, reply](const PackageDetails &details){ reply->push(headerWidgets(details)); reply->push(installedActionButtonWidgets()); reply->push(descriptionWidgets(details)); }, [this, reply](const ReviewList& reviewlist, click::Reviews::Error error) { if (error == click::Reviews::Error::NoError) { reply->push(reviewsWidgets(reviewlist)); } else { qDebug() << "There was an error getting reviews for:" << result["name"].get_string().c_str(); } reply->finished(); }); } scopes::PreviewWidgetList InstalledPreview::installedActionButtonWidgets() { scopes::PreviewWidgetList widgets; scopes::PreviewWidget buttons("buttons", "actions"); scopes::VariantBuilder builder; builder.add_tuple( { {"id", scopes::Variant(click::Preview::Actions::OPEN_CLICK)}, {"label", scopes::Variant(_("Open"))} }); builder.add_tuple( { {"id", scopes::Variant(click::Preview::Actions::UNINSTALL_CLICK)}, {"label", scopes::Variant(_("Uninstall"))} }); buttons.add_attribute_value("actions", builder.end()); widgets.push_back(buttons); return widgets; } // class PurchasingPreview PurchasingPreview::PurchasingPreview(const unity::scopes::Result& result, const QSharedPointer& client) : Preview(result, client) { } PurchasingPreview::~PurchasingPreview() { } void PurchasingPreview::run(unity::scopes::PreviewReplyProxy const& reply) { populateDetails([this, reply](const PackageDetails &details){ reply->push(purchasingWidgets(details)); }, [this, reply](const click::ReviewList&, click::Reviews::Error) { reply->finished(); }); } scopes::PreviewWidgetList PurchasingPreview::purchasingWidgets(const PackageDetails &/*details*/) { scopes::PreviewWidgetList widgets; return widgets; } // class UninstallConfirmationPreview UninstallConfirmationPreview::UninstallConfirmationPreview(const unity::scopes::Result& result) : Preview(result) { } UninstallConfirmationPreview::~UninstallConfirmationPreview() { } void UninstallConfirmationPreview::run(unity::scopes::PreviewReplyProxy const& reply) { // NOTE: no need to populateDetails() here. scopes::PreviewWidgetList widgets; scopes::PreviewWidget header("hdr", "header"); header.add_attribute_value("title", scopes::Variant(_("Confirmation"))); header.add_attribute_value("subtitle", scopes::Variant(_("Uninstalling this app will delete all the related information. Are you sure you want to uninstall?"))); // TODO: wording needs review. see bug LP: #1234211 widgets.push_back(header); scopes::PreviewWidget buttons("buttons", "actions"); scopes::VariantBuilder builder; builder.add_tuple({ {"id", scopes::Variant(click::Preview::Actions::CLOSE_PREVIEW)}, // TODO: see bug LP: #1289434 {"label", scopes::Variant(_("Not anymore"))} }); builder.add_tuple({ {"id", scopes::Variant(click::Preview::Actions::CONFIRM_UNINSTALL)}, {"label", scopes::Variant(_("Yes Uninstall"))} }); buttons.add_attribute_value("actions", builder.end()); widgets.push_back(buttons); reply->push(widgets); } // class UninstalledPreview UninstalledPreview::UninstalledPreview(const unity::scopes::Result& result, const QSharedPointer& client) : Preview(result, client) { } UninstalledPreview::~UninstalledPreview() { } void UninstalledPreview::run(unity::scopes::PreviewReplyProxy const& reply) { qDebug() << "in UninstalledPreview::run, about to populate details"; populateDetails([this, reply](const PackageDetails &details){ reply->push(headerWidgets(details)); reply->push(uninstalledActionButtonWidgets(details)); reply->push(descriptionWidgets(details)); }, [this, reply](const ReviewList& reviewlist, click::Reviews::Error error) { if (error == click::Reviews::Error::NoError) { reply->push(reviewsWidgets(reviewlist)); } else { qDebug() << "There was an error getting reviews for:" << result["name"].get_string().c_str(); } reply->finished(); }); } scopes::PreviewWidgetList UninstalledPreview::uninstalledActionButtonWidgets(const PackageDetails &details) { scopes::PreviewWidgetList widgets; scopes::PreviewWidget buttons("buttons", "actions"); scopes::VariantBuilder builder; builder.add_tuple( { {"id", scopes::Variant(click::Preview::Actions::INSTALL_CLICK)}, {"label", scopes::Variant(_("Install"))}, {"download_url", scopes::Variant(details.download_url)} }); buttons.add_attribute_value("actions", builder.end()); widgets.push_back(buttons); return widgets; } // class UninstallingPreview : public UninstalledPreview // TODO: this class should be removed once uninstall() is handled elsewhere. UninstallingPreview::UninstallingPreview(const unity::scopes::Result& result, const QSharedPointer& client) : UninstalledPreview(result, client) { } UninstallingPreview::~UninstallingPreview() { } void UninstallingPreview::run(unity::scopes::PreviewReplyProxy const& reply) { qDebug() << "in UninstallingPreview::run, calling uninstall"; uninstall(); qDebug() << "in UninstallingPreview::run, calling UninstalledPreview::run()"; UninstalledPreview::run(reply); } void UninstallingPreview::uninstall() { click::Package package; package.title = result.title(); package.name = result["name"].get_string(); package.version = result["version"].get_string(); qt::core::world::enter_with_task([this, package] (qt::core::world::Environment& /*env*/) { click::PackageManager manager; manager.uninstall(package, [&](int code, std::string stderr_content) { if (code != 0) { qDebug() << "Error removing package:" << stderr_content.c_str(); } else { qDebug() << "successfully removed package"; } } ); }); } } // namespace click unity-scope-click-0.1+14.04.20140417/scope/click/ubuntuone_credentials.h0000644000015301777760000000416612323764232026277 0ustar pbusernogroup00000000000000/* * Copyright (C) 2014 Canonical Ltd. * * This program is free software: you can redistribute it and/or modify it * under the terms of the GNU General Public License version 3, as published * by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranties of * MERCHANTABILITY, SATISFACTORY QUALITY, or FITNESS FOR A PARTICULAR * PURPOSE. See the GNU General Public License for more details. * * You should have received a copy of the GNU General Public License along * with this program. If not, see . * * In addition, as a special exception, the copyright holders give * permission to link the code of portions of this program with the * OpenSSL library under certain conditions as described in each * individual source file, and distribute linked combinations * including the two. * You must obey the GNU General Public License in all respects * for all of the code used other than OpenSSL. If you modify * file(s) with this exception, you may extend this exception to your * version of the file(s), but you are not obligated to do so. If you * do not wish to do so, delete this exception statement from your * version. If you delete this exception statement from all source * files in the program, then also delete it here. */ #ifndef _UBUNTUONE_CREDENTIALS_H_ #define _UBUNTUONE_CREDENTIALS_H_ #include #include namespace click { class CredentialsService : public UbuntuOne::SSOService { Q_OBJECT public: CredentialsService(); CredentialsService(const CredentialsService&) = delete; virtual ~CredentialsService(); CredentialsService& operator=(const CredentialsService&) = delete; virtual void getCredentials(); virtual void invalidateCredentials(); signals: void credentialsDeleted(); void credentialsFound(const UbuntuOne::Token& token); void credentialsNotFound(); private: QScopedPointer ssoService; }; // CredentialsService } // namespace click #endif /* _UBUNTUONE_CREDENTIALS_H_ */ unity-scope-click-0.1+14.04.20140417/scope/click/smartconnect.h0000644000015301777760000000423112323764232024367 0ustar pbusernogroup00000000000000/* * Copyright (C) 2014 Canonical Ltd. * * This program is free software: you can redistribute it and/or modify it * under the terms of the GNU General Public License version 3, as published * by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranties of * MERCHANTABILITY, SATISFACTORY QUALITY, or FITNESS FOR A PARTICULAR * PURPOSE. See the GNU General Public License for more details. * * You should have received a copy of the GNU General Public License along * with this program. If not, see . * * In addition, as a special exception, the copyright holders give * permission to link the code of portions of this program with the * OpenSSL library under certain conditions as described in each * individual source file, and distribute linked combinations * including the two. * You must obey the GNU General Public License in all respects * for all of the code used other than OpenSSL. If you modify * file(s) with this exception, you may extend this exception to your * version of the file(s), but you are not obligated to do so. If you * do not wish to do so, delete this exception statement from your * version. If you delete this exception statement from all source * files in the program, then also delete it here. */ #ifndef SMARTCONNECT_H #define SMARTCONNECT_H #include #include #include namespace click { namespace utils { class SmartConnect : public QObject { Q_OBJECT QList connections; virtual void cleanup(); private slots: void disconnectAll(); public: explicit SmartConnect(QObject *parent = 0); template void connect(SenderType* sender, SignalType signal, SlotType slot) { connections.append(QObject::connect(sender, signal, slot)); connections.append(QObject::connect(sender, signal, this, &SmartConnect::disconnectAll)); } }; } // namespace utils } // namespace click #endif // SMARTCONNECT_H unity-scope-click-0.1+14.04.20140417/scope/click/network_access_manager.cpp0000644000015301777760000000726312323764232026736 0ustar pbusernogroup00000000000000/* * Copyright (C) 2014 Canonical Ltd. * * This program is free software: you can redistribute it and/or modify it * under the terms of the GNU General Public License version 3, as published * by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranties of * MERCHANTABILITY, SATISFACTORY QUALITY, or FITNESS FOR A PARTICULAR * PURPOSE. See the GNU General Public License for more details. * * You should have received a copy of the GNU General Public License along * with this program. If not, see . * * In addition, as a special exception, the copyright holders give * permission to link the code of portions of this program with the * OpenSSL library under certain conditions as described in each * individual source file, and distribute linked combinations * including the two. * You must obey the GNU General Public License in all respects * for all of the code used other than OpenSSL. If you modify * file(s) with this exception, you may extend this exception to your * version of the file(s), but you are not obligated to do so. If you * do not wish to do so, delete this exception statement from your * version. If you delete this exception statement from all source * files in the program, then also delete it here. */ #include "network_access_manager.h" click::network::Reply::Reply(QNetworkReply* reply) : reply(reply) { connect(this->reply.data(), &QNetworkReply::finished, this, &Reply::finished); typedef void(QNetworkReply::*QNetworkReplyErrorSignal)(QNetworkReply::NetworkError); connect(this->reply.data(), static_cast(&QNetworkReply::error), this, &Reply::error); } click::network::Reply::~Reply() { } void click::network::Reply::abort() { reply->abort(); } QByteArray click::network::Reply::readAll() { return reply->readAll(); } QVariant click::network::Reply::attribute(QNetworkRequest::Attribute code) { return reply->attribute(code); } bool click::network::Reply::hasRawHeader(const QByteArray &headerName) { return reply->hasRawHeader(headerName); } QString click::network::Reply::rawHeader(const QByteArray &headerName) { return reply->rawHeader(headerName); } QList> click::network::Reply::rawHeaderPairs() { return reply->rawHeaderPairs(); } QString click::network::Reply::errorString() { return reply->errorString(); } click::network::Reply::Reply() { } namespace { QNetworkAccessManager& networkAccessManagerInstance() { static QNetworkAccessManager nam; return nam; } } QSharedPointer click::network::AccessManager::get(QNetworkRequest& request) { return QSharedPointer(new click::network::Reply(networkAccessManagerInstance().get(request))); } QSharedPointer click::network::AccessManager::head(QNetworkRequest& request) { return QSharedPointer(new click::network::Reply(networkAccessManagerInstance().head(request))); } QSharedPointer click::network::AccessManager::post(QNetworkRequest& request, QByteArray& data) { return QSharedPointer(new click::network::Reply(networkAccessManagerInstance().post(request, data))); } QSharedPointer click::network::AccessManager::sendCustomRequest(QNetworkRequest& request, QByteArray& verb, QIODevice *data) { return QSharedPointer(new click::network::Reply(networkAccessManagerInstance().sendCustomRequest(request, verb, data))); } unity-scope-click-0.1+14.04.20140417/scope/click/click-i18n.h0000644000015301777760000000275512323764232023542 0ustar pbusernogroup00000000000000/* * Copyright (C) 2014 Canonical Ltd. * * This program is free software: you can redistribute it and/or modify it * under the terms of the GNU General Public License version 3, as published * by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranties of * MERCHANTABILITY, SATISFACTORY QUALITY, or FITNESS FOR A PARTICULAR * PURPOSE. See the GNU General Public License for more details. * * You should have received a copy of the GNU General Public License along * with this program. If not, see . * * In addition, as a special exception, the copyright holders give * permission to link the code of portions of this program with the * OpenSSL library under certain conditions as described in each * individual source file, and distribute linked combinations * including the two. * You must obey the GNU General Public License in all respects * for all of the code used other than OpenSSL. If you modify * file(s) with this exception, you may extend this exception to your * version of the file(s), but you are not obligated to do so. If you * do not wish to do so, delete this exception statement from your * version. If you delete this exception statement from all source * files in the program, then also delete it here. */ #ifndef CLICKI18N_H #define CLICKI18N_H #include #define _(value) dgettext(GETTEXT_PACKAGE, value) #endif unity-scope-click-0.1+14.04.20140417/scope/click/scope.h0000644000015301777760000000517512323764232023010 0ustar pbusernogroup00000000000000/* * Copyright (C) 2014 Canonical Ltd. * * This program is free software: you can redistribute it and/or modify it * under the terms of the GNU General Public License version 3, as published * by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranties of * MERCHANTABILITY, SATISFACTORY QUALITY, or FITNESS FOR A PARTICULAR * PURPOSE. See the GNU General Public License for more details. * * You should have received a copy of the GNU General Public License along * with this program. If not, see . * * In addition, as a special exception, the copyright holders give * permission to link the code of portions of this program with the * OpenSSL library under certain conditions as described in each * individual source file, and distribute linked combinations * including the two. * You must obey the GNU General Public License in all respects * for all of the code used other than OpenSSL. If you modify * file(s) with this exception, you may extend this exception to your * version of the file(s), but you are not obligated to do so. If you * do not wish to do so, delete this exception statement from your * version. If you delete this exception statement from all source * files in the program, then also delete it here. */ #ifndef CLICK_SCOPE_H #define CLICK_SCOPE_H #include #include #include #include "network_access_manager.h" #include "ubuntuone_credentials.h" #include "webclient.h" namespace scopes = unity::scopes; namespace click { class Scope : public scopes::ScopeBase { public: Scope(); ~Scope(); virtual int start(std::string const&, scopes::RegistryProxy const&) override; virtual void run() override; virtual void stop() override; virtual scopes::SearchQueryBase::UPtr search(scopes::CannedQuery const& q, scopes::SearchMetadata const&) override; unity::scopes::PreviewQueryBase::UPtr preview(const unity::scopes::Result&, const unity::scopes::ActionMetadata&) override; virtual unity::scopes::ActivationQueryBase::UPtr perform_action(unity::scopes::Result const& result, unity::scopes::ActionMetadata const& metadata, std::string const& widget_id, std::string const& action_id) override; private: QSharedPointer nam; QSharedPointer sso; QSharedPointer client; std::string installApplication(unity::scopes::Result const& result); }; } #endif // CLICK_SCOPE_H unity-scope-click-0.1+14.04.20140417/scope/click/key_file_locator.cpp0000644000015301777760000000640312323764232025537 0ustar pbusernogroup00000000000000/* * Copyright (C) 2014 Canonical Ltd. * * This program is free software: you can redistribute it and/or modify it * under the terms of the GNU General Public License version 3, as published * by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranties of * MERCHANTABILITY, SATISFACTORY QUALITY, or FITNESS FOR A PARTICULAR * PURPOSE. See the GNU General Public License for more details. * * You should have received a copy of the GNU General Public License along * with this program. If not, see . * * In addition, as a special exception, the copyright holders give * permission to link the code of portions of this program with the * OpenSSL library under certain conditions as described in each * individual source file, and distribute linked combinations * including the two. * You must obey the GNU General Public License in all respects * for all of the code used other than OpenSSL. If you modify * file(s) with this exception, you may extend this exception to your * version of the file(s), but you are not obligated to do so. If you * do not wish to do so, delete this exception statement from your * version. If you delete this exception statement from all source * files in the program, then also delete it here. */ #include "key_file_locator.h" #include #include #include #include #include #include #include namespace { static const QString NON_CLICK_PATH("/usr/share/applications"); void find_apps_in_dir(const QString& dir_path, const click::KeyFileLocator::Enumerator& enumerator) { QDir dir(dir_path, "*.desktop", QDir::Unsorted, QDir::Readable | QDir::Files); QStringList entries = dir.entryList(); for (int i = 0; i < entries.size(); ++i) { QString filename = entries.at(i); QString full_path = dir.absoluteFilePath(filename); try { enumerator(unity::util::IniParser(full_path.toUtf8().data()), filename.toUtf8().data()); } catch (const unity::FileException& file_exp) { qWarning() << "Error reading file:" << file_exp.to_string().c_str(); } } } } const std::string& click::KeyFileLocator::systemApplicationsDirectory() { static const std::string s{"/usr/share/applications"}; return s; } const std::string& click::KeyFileLocator::userApplicationsDirectory() { static const std::string s { qPrintable(QStandardPaths::writableLocation(QStandardPaths::GenericDataLocation) + "/applications") }; return s; } click::KeyFileLocator::KeyFileLocator( const std::string& systemApplicationsDir, const std::string& userApplicationsDir) : systemApplicationsDir(systemApplicationsDir), userApplicationsDir(userApplicationsDir) { } void click::KeyFileLocator::enumerateKeyFilesForInstalledApplications( const click::KeyFileLocator::Enumerator& enumerator) { find_apps_in_dir(QString::fromStdString(systemApplicationsDir), enumerator); find_apps_in_dir(QString::fromStdString(userApplicationsDir), enumerator); } unity-scope-click-0.1+14.04.20140417/scope/click/index.cpp0000644000015301777760000003056412323764232023341 0ustar pbusernogroup00000000000000/* * Copyright (C) 2014 Canonical Ltd. * * This program is free software: you can redistribute it and/or modify it * under the terms of the GNU General Public License version 3, as published * by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranties of * MERCHANTABILITY, SATISFACTORY QUALITY, or FITNESS FOR A PARTICULAR * PURPOSE. See the GNU General Public License for more details. * * You should have received a copy of the GNU General Public License along * with this program. If not, see . * * In addition, as a special exception, the copyright holders give * permission to link the code of portions of this program with the * OpenSSL library under certain conditions as described in each * individual source file, and distribute linked combinations * including the two. * You must obey the GNU General Public License in all respects * for all of the code used other than OpenSSL. If you modify * file(s) with this exception, you may extend this exception to your * version of the file(s), but you are not obligated to do so. If you * do not wish to do so, delete this exception statement from your * version. If you delete this exception statement from all source * files in the program, then also delete it here. */ #include #include #include #include #include #include "download-manager.h" #include "index.h" #include "application.h" namespace json = Json; namespace click { bool operator==(const Package& lhs, const Package& rhs) { return lhs.name == rhs.name && lhs.title == rhs.title && lhs.price == rhs.price && lhs.icon_url == rhs.icon_url; } bool operator==(const PackageDetails& lhs, const PackageDetails& rhs) { return lhs.package == rhs.package && lhs.description == rhs.description && lhs.download_url == rhs.download_url && lhs.rating == rhs.rating && // TODO: keywords should be a list of strings lhs.keywords == rhs.keywords && lhs.terms_of_service == rhs.terms_of_service && lhs.license == rhs.license && lhs.publisher == rhs.publisher && lhs.main_screenshot_url == rhs.main_screenshot_url && // TODO: more_screenshots_urls should be a list of strings lhs.more_screenshots_urls == rhs.more_screenshots_urls && // TODO: binary_filesize should be a int/long lhs.binary_filesize == rhs.binary_filesize && lhs.version == rhs.version && lhs.framework == rhs.framework; } QDebug operator<< (QDebug d, const std::string &s) { d << QString::fromStdString(s); return d; } bool operator==(const Application& lhs, const Application& rhs) { return lhs.name == rhs.name && lhs.title == rhs.title && lhs.description == rhs.description && lhs.main_screenshot == rhs.main_screenshot && lhs.icon_url == rhs.icon_url; } void PackageManager::uninstall(const Package& package, std::function callback) { std::string package_id = package.name + ";" + package.version + ";all;local:click"; std::string command = "pkcon -p remove " + package_id; execute_uninstall_command(command, callback); } void PackageManager::execute_uninstall_command(const std::string& command, std::function callback) { QSharedPointer process(new QProcess()); typedef void(QProcess::*QProcessFinished)(int, QProcess::ExitStatus); typedef void(QProcess::*QProcessError)(QProcess::ProcessError); QObject::connect(process.data(), static_cast(&QProcess::finished), [process, callback](int code, QProcess::ExitStatus status) { Q_UNUSED(status); qDebug() << "command finished with exit code:" << code; callback(code, process.data()->readAllStandardError().data()); if (code == 0) { QProcess::execute(DBUSSEND_COMMAND); } } ); QObject::connect(process.data(), static_cast(&QProcess::error), [process, callback](QProcess::ProcessError error) { qCritical() << "error running command:" << error; callback(-255 + error, process.data()->readAllStandardError().data()); } ); qDebug() << "Running command:" << command.c_str(); process.data()->start(command.c_str()); } PackageList package_list_from_json(const std::string& json) { std::istringstream is(json); PackageList pl; json::Reader reader; json::Value root; if (!reader.parse(json, root)) { throw std::runtime_error(reader.getFormattedErrorMessages()); } for (uint i = 0; i < root.size(); i++) { Package p; json::Value item = root[i]; p.name = item[Package::JsonKeys::name].asString(); p.title = item[Package::JsonKeys::title].asString(); p.price = item[Package::JsonKeys::price].asDouble(); p.icon_url = item[Package::JsonKeys::icon_url].asString(); p.url = item[Package::JsonKeys::resource_url].asString(); pl.push_back(p); } return pl; } PackageDetails PackageDetails::from_json(const std::string &json) { PackageDetails details; try { json::Reader reader; json::Value root; if (!reader.parse(json, root)) throw std::runtime_error(reader.getFormattedErrorMessages()); // Mandatory details go here. That is, asString() will throw as we // do not provide a default value if a value with the given key does not exist. details.package.name = root[Package::JsonKeys::name].asString(); details.package.title = root[Package::JsonKeys::title].asString(); details.package.icon_url = root[Package::JsonKeys::icon_url].asString(); details.package.price = root[Package::JsonKeys::price].asDouble(); details.description = root[JsonKeys::description].asString(); details.download_url = root[JsonKeys::download_url].asString(); details.license = root[JsonKeys::license].asString(); // Optional details go here. if (root[JsonKeys::version].isString()) details.version = root[JsonKeys::version].asString(); if (root[JsonKeys::rating].isNumeric()) details.rating = root[JsonKeys::rating].asDouble(); if (root[JsonKeys::keywords].isString()) details.keywords = root[JsonKeys::keywords].asString(); if (root[JsonKeys::terms_of_service].isString()) details.terms_of_service = root[JsonKeys::terms_of_service].asString(); if (root[JsonKeys::publisher].isString()) details.publisher = root[JsonKeys::publisher].asString(); if (root[JsonKeys::main_screenshot_url].isString()) details.main_screenshot_url = root[JsonKeys::main_screenshot_url].asString(); auto screenshots = root[JsonKeys::more_screenshot_urls]; for (uint i = 0; i < screenshots.size(); i++) { auto scr = screenshots[i].asString(); // more_screenshot_urls may contain main_screenshot_url, if so, skip it if (scr != details.main_screenshot_url) { details.more_screenshots_urls.push_back(scr); } } if (root[JsonKeys::binary_filesize].isIntegral()) details.binary_filesize = root[JsonKeys::binary_filesize].asUInt64(); if (root[JsonKeys::framework].isString()) details.framework = root[JsonKeys::framework].asString(); } catch(const std::exception& e) { std::cerr << "PackageDetails::loadJson: Exception thrown while decoding JSON: " << e.what() << std::endl; } catch(...) { std::cerr << "PackageDetails::loadJson: Exception thrown while decoding JSON." << std::endl; } return details; } std::string print_string_if_not_empty(const std::string& s) { return s.empty() ? "n/a" : s; } std::string print_list_if_not_empty(const std::list& li) { std::stringstream s; s << "["; if (!li.empty()) { auto it = li.begin(); s << print_string_if_not_empty(*it); ++it; while (it != li.end()) { s << ", " << print_string_if_not_empty(*it); ++it; } } s << "]"; return s.str(); } std::ostream& operator<<(std::ostream& out, const click::PackageDetails& details) { out << "(" << print_string_if_not_empty(details.package.name) << ", " << print_string_if_not_empty(details.package.title) << ", " << print_string_if_not_empty(details.package.icon_url) << ", " << print_string_if_not_empty(details.description) << ", " << print_string_if_not_empty(details.download_url) << ", " << details.rating << ", " << print_string_if_not_empty(details.keywords) << ", " << print_string_if_not_empty(details.terms_of_service) << ", " << print_string_if_not_empty(details.license) << ", " << print_string_if_not_empty(details.publisher) << ", " << print_string_if_not_empty(details.main_screenshot_url) << ", " << print_list_if_not_empty(details.more_screenshots_urls) << ", " << details.binary_filesize << ", " << print_string_if_not_empty(details.version) << ", " << print_string_if_not_empty(details.framework) << ")"; return out; } std::ostream& operator<<(std::ostream& out, const click::Application& app) { out << "(" << print_string_if_not_empty(app.name) << ", " << print_string_if_not_empty(app.title) << ", " << app.price << ", " << print_string_if_not_empty(app.icon_url) << ", " << print_string_if_not_empty(app.url) << ", " << print_string_if_not_empty(app.version) << ", " << print_string_if_not_empty(app.description) << ", " << print_string_if_not_empty(app.main_screenshot) << ")"; return out; } Index::Index(const QSharedPointer& client) : client(client) { } click::web::Cancellable Index::search (const std::string& query, std::function callback) { click::web::CallParams params; params.add(click::QUERY_ARGNAME, query.c_str()); QSharedPointer response(client->call( click::SEARCH_BASE_URL + click::SEARCH_PATH, params)); QObject::connect(response.data(), &click::web::Response::finished, [=](QString reply) { click::PackageList pl = click::package_list_from_json(reply.toUtf8().constData()); callback(pl); }); QObject::connect(response.data(), &click::web::Response::error, [=](QString /*description*/) { qDebug() << "No packages found due to network error"; click::PackageList pl; callback(pl); }); return click::web::Cancellable(response); } click::web::Cancellable Index::get_details (const std::string& package_name, std::function callback) { QSharedPointer response = client->call (click::SEARCH_BASE_URL + click::DETAILS_PATH + package_name); qDebug() << "getting details for" << package_name.c_str(); QObject::connect(response.data(), &click::web::Response::finished, [=](const QByteArray reply) { qDebug() << "index, response finished:" << reply.toPercentEncoding(" {},=:\n\"'"); click::PackageDetails d = click::PackageDetails::from_json(reply.constData()); qDebug() << "index, details title:" << QByteArray(d.package.title.c_str()).toPercentEncoding(" "); callback(d, click::Index::Error::NoError); }); QObject::connect(response.data(), &click::web::Response::error, [=](QString /*description*/) { qDebug() << "Cannot get package details due to network error"; callback(PackageDetails(), click::Index::Error::NetworkError); }); return click::web::Cancellable(response); } Index::~Index() { } } // namespace click #include "index.moc" unity-scope-click-0.1+14.04.20140417/scope/click/webclient.h0000644000015301777760000000675412323764232023657 0ustar pbusernogroup00000000000000/* * Copyright (C) 2014 Canonical Ltd. * * This program is free software: you can redistribute it and/or modify it * under the terms of the GNU General Public License version 3, as published * by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranties of * MERCHANTABILITY, SATISFACTORY QUALITY, or FITNESS FOR A PARTICULAR * PURPOSE. See the GNU General Public License for more details. * * You should have received a copy of the GNU General Public License along * with this program. If not, see . * * In addition, as a special exception, the copyright holders give * permission to link the code of portions of this program with the * OpenSSL library under certain conditions as described in each * individual source file, and distribute linked combinations * including the two. * You must obey the GNU General Public License in all respects * for all of the code used other than OpenSSL. If you modify * file(s) with this exception, you may extend this exception to your * version of the file(s), but you are not obligated to do so. If you * do not wish to do so, delete this exception statement from your * version. If you delete this exception statement from all source * files in the program, then also delete it here. */ #ifndef CLICK_WEBCLIENT_H #define CLICK_WEBCLIENT_H #include #include #include #include #include #include #include "ubuntuone_credentials.h" namespace click { namespace network { class AccessManager; class Reply; } namespace web { const std::string AUTHORIZATION = "Authorization"; class Client; class CallParams { QUrlQuery query; friend class Client; public: void add(const std::string& key, const std::string& value); bool operator==(const CallParams &other) const; }; class Response : public QObject { Q_OBJECT public: Response(const QSharedPointer& buffer, QObject* parent=0); void setReply(QSharedPointer reply); virtual void abort(); virtual ~Response(); public slots: void replyFinished(); void errorHandler(QNetworkReply::NetworkError network_error); signals: void finished(QByteArray result); void error(QString description); private: QSharedPointer reply; QSharedPointer buffer; }; class Cancellable { protected: QSharedPointer response; public: Cancellable() {} Cancellable(QSharedPointer response) : response(response) {} virtual void cancel() { if (response) {response->abort();} } virtual ~Cancellable() {} }; class Client { public: Client(const QSharedPointer& networkAccessManager, const QSharedPointer& sso); virtual ~Client(); virtual QSharedPointer call( const std::string& iri, const CallParams& params = CallParams()); virtual QSharedPointer call( const std::string& iri, const std::string& method, bool sign = false, const std::map& headers = std::map(), const std::string& data = "", const CallParams& params = CallParams()); private: struct Private; QScopedPointer impl; }; } } #endif // CLICK_WEBCLIENT_H unity-scope-click-0.1+14.04.20140417/scope/click/scope.cpp0000644000015301777760000002367412323764232023347 0ustar pbusernogroup00000000000000/* * Copyright (C) 2014 Canonical Ltd. * * This program is free software: you can redistribute it and/or modify it * under the terms of the GNU General Public License version 3, as published * by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranties of * MERCHANTABILITY, SATISFACTORY QUALITY, or FITNESS FOR A PARTICULAR * PURPOSE. See the GNU General Public License for more details. * * You should have received a copy of the GNU General Public License along * with this program. If not, see . * * In addition, as a special exception, the copyright holders give * permission to link the code of portions of this program with the * OpenSSL library under certain conditions as described in each * individual source file, and distribute linked combinations * including the two. * You must obey the GNU General Public License in all respects * for all of the code used other than OpenSSL. If you modify * file(s) with this exception, you may extend this exception to your * version of the file(s), but you are not obligated to do so. If you * do not wish to do so, delete this exception statement from your * version. If you delete this exception statement from all source * files in the program, then also delete it here. */ #include "qtbridge.h" #include "scope.h" #include "query.h" #include "preview.h" #include "network_access_manager.h" #include "key_file_locator.h" #include "interface.h" #include #include #include "click-i18n.h" namespace { click::Interface& clickInterfaceInstance() { static QSharedPointer keyFileLocator(new click::KeyFileLocator()); static click::Interface iface(keyFileLocator); return iface; } } class ScopeActivation : public unity::scopes::ActivationQueryBase { unity::scopes::ActivationResponse activate() override { auto response = unity::scopes::ActivationResponse(status_); response.set_scope_data(unity::scopes::Variant(hints_)); return response; } public: void setStatus(unity::scopes::ActivationResponse::Status status) { status_ = status; } void setHint(std::string key, unity::scopes::Variant value) { hints_[key] = value; } private: unity::scopes::ActivationResponse::Status status_ = unity::scopes::ActivationResponse::Status::ShowPreview; unity::scopes::VariantMap hints_; }; click::Scope::Scope() { nam = QSharedPointer(new click::network::AccessManager()); sso = QSharedPointer(new click::CredentialsService()); client = QSharedPointer(new click::web::Client(nam, sso)); } click::Scope::~Scope() { } int click::Scope::start(std::string const&, scopes::RegistryProxy const&) { setlocale(LC_ALL, ""); bindtextdomain(GETTEXT_PACKAGE, GETTEXT_LOCALEDIR); bind_textdomain_codeset(GETTEXT_PACKAGE, "UTF-8"); return VERSION; } void click::Scope::run() { static const int zero = 0; auto emptyCb = [this]() { }; qt::core::world::build_and_run(zero, nullptr, emptyCb); } void click::Scope::stop() { qt::core::world::destroy(); } scopes::SearchQueryBase::UPtr click::Scope::search(unity::scopes::CannedQuery const& q, scopes::SearchMetadata const& metadata) { return scopes::SearchQueryBase::UPtr(new click::Query(q.query_string(), metadata)); } unity::scopes::PreviewQueryBase::UPtr click::Scope::preview(const unity::scopes::Result& result, const unity::scopes::ActionMetadata& metadata) { qDebug() << "Scope::preview() called."; std::string action_id = ""; std::string download_url = ""; if (metadata.scope_data().which() != scopes::Variant::Type::Null) { auto metadict = metadata.scope_data().get_dict(); if (metadict.count(click::Preview::Actions::DOWNLOAD_FAILED) != 0) { return scopes::PreviewQueryBase::UPtr{new DownloadErrorPreview(result)}; } else if (metadict.count(click::Preview::Actions::DOWNLOAD_COMPLETED) != 0 || metadict.count(click::Preview::Actions::CLOSE_PREVIEW) != 0) { qDebug() << "in Scope::preview(), metadata has download_completed=" << metadict.count(click::Preview::Actions::DOWNLOAD_COMPLETED) << " and close_preview=" << metadict.count(click::Preview::Actions::CLOSE_PREVIEW); return scopes::PreviewQueryBase::UPtr{new InstalledPreview(result, client)}; } else if (metadict.count("action_id") != 0 && metadict.count("download_url") != 0) { action_id = metadict["action_id"].get_string(); download_url = metadict["download_url"].get_string(); if (action_id == click::Preview::Actions::INSTALL_CLICK) { return scopes::PreviewQueryBase::UPtr{new InstallingPreview(download_url, result, client, nam)}; } else { qWarning() << "unexpected action id " << QString::fromStdString(action_id) << " given with download_url" << QString::fromStdString(download_url); return scopes::PreviewQueryBase::UPtr{new UninstalledPreview(result, client)}; } } else if (metadict.count(click::Preview::Actions::UNINSTALL_CLICK) != 0) { return scopes::PreviewQueryBase::UPtr{ new UninstallConfirmationPreview(result)}; } else if (metadict.count(click::Preview::Actions::CONFIRM_UNINSTALL) != 0) { return scopes::PreviewQueryBase::UPtr{new UninstallingPreview(result, client)}; } else { qWarning() << "preview() called with unexpected metadata. returning uninstalled preview"; return scopes::PreviewQueryBase::UPtr{new UninstalledPreview(result, client)}; } } else { // metadata.scope_data() is Null, so we return an appropriate "default" preview: if (result["installed"].get_bool() == true) { return scopes::PreviewQueryBase::UPtr{new InstalledPreview(result, client)}; } else { return scopes::PreviewQueryBase::UPtr{new UninstalledPreview(result, client)}; } } } unity::scopes::ActivationQueryBase::UPtr click::Scope::perform_action(unity::scopes::Result const& result, unity::scopes::ActionMetadata const& metadata, std::string const& /* widget_id */, std::string const& action_id) { auto activation = new ScopeActivation(); qDebug() << "perform_action called with action_id" << QString().fromStdString(action_id); if (action_id == click::Preview::Actions::OPEN_CLICK) { QString app_url = QString::fromStdString(result.uri()); if (!app_url.startsWith("application:///")) { qt::core::world::enter_with_task([this, result] (qt::core::world::Environment& /*env*/) { clickInterfaceInstance().get_dotdesktop_filename(result["name"].get_string(), [] (std::string val, click::ManifestError error){ if (error == click::ManifestError::NoError) { std::string uri = "application:///" + val; url_dispatch_send(uri.c_str() , NULL, NULL); } } ); }); activation->setStatus(unity::scopes::ActivationResponse::Status::HideDash); } else { activation->setStatus(unity::scopes::ActivationResponse::Status::NotHandled); } } else if (action_id == click::Preview::Actions::INSTALL_CLICK) { std::string download_url = metadata.scope_data().get_dict()["download_url"].get_string(); qDebug() << "the download url is: " << QString::fromStdString(download_url); activation->setHint("download_url", unity::scopes::Variant(download_url)); activation->setHint("action_id", unity::scopes::Variant(action_id)); qDebug() << "returning ShowPreview"; activation->setStatus(unity::scopes::ActivationResponse::Status::ShowPreview); } else if (action_id == click::Preview::Actions::DOWNLOAD_FAILED) { activation->setHint(click::Preview::Actions::DOWNLOAD_FAILED, unity::scopes::Variant(true)); activation->setStatus(unity::scopes::ActivationResponse::Status::ShowPreview); } else if (action_id == click::Preview::Actions::DOWNLOAD_COMPLETED) { activation->setHint(click::Preview::Actions::DOWNLOAD_COMPLETED, unity::scopes::Variant(true)); activation->setStatus(unity::scopes::ActivationResponse::Status::ShowPreview); } else if (action_id == click::Preview::Actions::UNINSTALL_CLICK) { activation->setHint(click::Preview::Actions::UNINSTALL_CLICK, unity::scopes::Variant(true)); activation->setStatus(unity::scopes::ActivationResponse::Status::ShowPreview); } else if (action_id == click::Preview::Actions::CLOSE_PREVIEW) { activation->setHint(click::Preview::Actions::CLOSE_PREVIEW, unity::scopes::Variant(true)); activation->setStatus(unity::scopes::ActivationResponse::Status::ShowPreview); } else if (action_id == click::Preview::Actions::CONFIRM_UNINSTALL) { activation->setHint(click::Preview::Actions::CONFIRM_UNINSTALL, unity::scopes::Variant(true)); activation->setStatus(unity::scopes::ActivationResponse::Status::ShowPreview); } else if (action_id == click::Preview::Actions::OPEN_ACCOUNTS) { std::string uri = "settings:///system/online-accounts"; url_dispatch_send(uri.c_str() , NULL, NULL); } return scopes::ActivationQueryBase::UPtr(activation); } #define EXPORT __attribute__ ((visibility ("default"))) extern "C" { EXPORT unity::scopes::ScopeBase* // cppcheck-suppress unusedFunction UNITY_SCOPE_CREATE_FUNCTION() { return new click::Scope(); } EXPORT void // cppcheck-suppress unusedFunction UNITY_SCOPE_DESTROY_FUNCTION(unity::scopes::ScopeBase* scope_base) { delete scope_base; } } unity-scope-click-0.1+14.04.20140417/scope/click/download-manager.h0000644000015301777760000000772512323764232025121 0ustar pbusernogroup00000000000000/* * Copyright (C) 2014 Canonical Ltd. * * This program is free software: you can redistribute it and/or modify it * under the terms of the GNU General Public License version 3, as published * by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranties of * MERCHANTABILITY, SATISFACTORY QUALITY, or FITNESS FOR A PARTICULAR * PURPOSE. See the GNU General Public License for more details. * * You should have received a copy of the GNU General Public License along * with this program. If not, see . * * In addition, as a special exception, the copyright holders give * permission to link the code of portions of this program with the * OpenSSL library under certain conditions as described in each * individual source file, and distribute linked combinations * including the two. * You must obey the GNU General Public License in all respects * for all of the code used other than OpenSSL. If you modify * file(s) with this exception, you may extend this exception to your * version of the file(s), but you are not obligated to do so. If you * do not wish to do so, delete this exception statement from your * version. If you delete this exception statement from all source * files in the program, then also delete it here. */ #ifndef CLICK_DOWNLOAD_MANAGER_H #define CLICK_DOWNLOAD_MANAGER_H #include "network_access_manager.h" #include "ubuntuone_credentials.h" #include #include #include #include #include using Ubuntu::DownloadManager::Download; namespace UbuntuOne { class Token; } namespace click { static const QString QUOTED_ARG0 = QStringLiteral("\"$0\""); // The dbus-send command to refresh the search results in the dash. static const QString DBUSSEND_COMMAND = QStringLiteral("dbus-send /com/canonical/unity/scopes com.canonical.unity.scopes.InvalidateResults string:clickscope"); const QByteArray& CLICK_TOKEN_HEADER(); class DownloadManager : public QObject { Q_OBJECT public: DownloadManager(const QSharedPointer& networkAccessManager, const QSharedPointer& ssoService, const QSharedPointer& systemDownloadManager, QObject *parent = 0); virtual ~DownloadManager(); public slots: virtual void startDownload(const QString& downloadUrl, const QString& appId); virtual void fetchClickToken(const QString& downloadUrl); signals: void credentialsNotFound(); void downloadStarted(const QString& downloadObjectPath); void downloadError(const QString& errorMessage); void clickTokenFetched(const QString& clickToken); void clickTokenFetchError(const QString& errorMessage); protected slots: virtual void handleCredentialsFound(const UbuntuOne::Token &token); virtual void handleCredentialsNotFound(); virtual void handleNetworkFinished(); virtual void handleNetworkError(QNetworkReply::NetworkError error); virtual void handleDownloadCreated(Download *download); virtual void handleClickTokenFetched(const QString& clickToken); virtual void handleClickTokenFetchError(const QString& errorMessage); protected: struct Private; QScopedPointer impl; }; enum class InstallError {NoError, CredentialsError, DownloadInstallError}; class Downloader { public: Downloader(const QSharedPointer& networkAccessManager); void get_download_progress(std::string package_name, const std::function& callback); void startDownload(std::string url, std::string package_name, const std::function)>& callback); private: QSharedPointer networkAccessManager; }; } #endif /* CLICK_DOWNLOAD_MANAGER_H */ unity-scope-click-0.1+14.04.20140417/scope/click/qtbridge.h0000644000015301777760000001445212323764240023475 0ustar pbusernogroup00000000000000/* * Copyright © 2014 Canonical Ltd. * * This program is free software: you can redistribute it and/or modify it * under the terms of the GNU Lesser General Public License version 3, * as published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public License * along with this program. If not, see . * * Authored by: Jussi Pakkanen * Thomas Voß */ #ifndef QT_CORE_WORLD_BRIDGE_H_ #define QT_CORE_WORLD_BRIDGE_H_ #include #include #include #include namespace qt { namespace core { namespace world { // Forward declaration to allow for friend declaration in HeapAllocatedObject. class Environment; } } /** * @brief Models an object allocated on the heap in qt world, but pulled over * to this world to be passed on to tasks. Please note that compilation will * abort if QObject is not a base of T. * * One other important thing to note: The destrucotor will throw a std::runtime_error * if a HeapAllocatedObject has not been free'd in Qt world and does _not_ have a parent * assigned. In that case, the object would be leaked. * */ template class HeapAllocatedObject { public: static_assert( std::is_base_of::value, "HeapAllocatedObject is only required to transport QObject" " subclasses over the world boundary."); /** Constructs an empty, invalid instance. */ HeapAllocatedObject() = default; /** HeapAllocatedObjects, i.e., their shared state, can be copied. */ HeapAllocatedObject(const HeapAllocatedObject& rhs) = default; /** HeapAllocatedObjects, i.e., their shared state can be assigned. */ HeapAllocatedObject& operator=(const HeapAllocatedObject& rhs) = default; /** HeapAllocatedObjects, i.e., their shared state can be compared for equality. */ inline bool operator==(const HeapAllocatedObject& rhs) const { return state == rhs.state || state->instance == rhs.state->instance; } /** Check if this object contains a valid instance. */ inline operator bool() const { return state && (state->instance != nullptr); } private: friend class qt::core::world::Environment; struct Private { Private(T* instance) : instance(instance) { } ~Private() { if (instance != nullptr && instance->parent() == nullptr) { std::cerr << "MEMORY LEAK: HeapAllocatedObject::Private::~Private: " "Giving up ownership on a QObject instance without a parent: " << std::string(qPrintable(instance->metaObject()->className())) << std::endl; // We allow leaking a bit of memory here instead of crashing the scope } } T* instance{nullptr}; }; HeapAllocatedObject(T* instance) : state(std::make_shared(instance)) { } std::shared_ptr state; }; namespace core { namespace world { /** * @brief The Environment class models the environment in the Qt world * that tasks operate under. */ class Environment : public QObject { Q_OBJECT public: /** * @brief Allocates subclasses of a QObject in the Qt world to make sure * thread assumptions are satisfied. * * @tparam Args Construction argument types to be passed to the class's c'tor. * @param args Construction arguments to be passed to the class's c'tor. */ template qt::HeapAllocatedObject allocate(Args... args) { return qt::HeapAllocatedObject{new T(args...)}; } /** * @brief Frees a previously allocated object in the Qt world. * @param object The object to be freed. */ template void free(const qt::HeapAllocatedObject& object) { delete object.state->instance; object.state->instance = nullptr; } /** * @brief Provides access to the instance contained in the object handle. * @param object The object containing the instance to be accessed. * @return A pointer to an instance of T, or nullptr. */ template T* resolve(const HeapAllocatedObject& object) { return object.state->instance; } protected: Environment(QObject* parent); }; /** * @brief Sets up the Qt core world and executes its event loop. Blocks until destroy() is called. * @param argc Number of arguments in argv. * @param argv Array of command-line arguments. * @param ready Functor be called when the world has been setup and is about to be executed. * @throw std::runtime_error in case of errors. */ void build_and_run(int argc, char** argv, const std::function& ready); /** * @brief Destroys the Qt core world and quits its event loop. */ void destroy(); /** * @brief Enters the Qt core world and schedules the given task for execution. * @param task The task to be executed in the Qt core world. * @return A std::future that can be waited for to synchronize to the world's internal event loop. */ std::future enter_with_task(const std::function& task); /** * @brief Enters the Qt core world and schedules the given task for execution. * @param task The task to be executed in the Qt core world. * @return A std::future that can be waited for to get hold of the result of the task. */ template inline std::future enter_with_task_and_expect_result(const std::function& task) { std::shared_ptr> promise = std::make_shared>(); std::future future = promise->get_future(); auto t = [promise, task](Environment& env) { try { promise->set_value(task(env)); } catch(...) { promise->set_exception(std::current_exception()); } }; enter_with_task(t); return future; } } } } #endif // QT_CORE_WORLD_BRIDGE_H_ unity-scope-click-0.1+14.04.20140417/scope/click/interface.cpp0000644000015301777760000003551712323764232024175 0ustar pbusernogroup00000000000000/* * Copyright (C) 2014 Canonical Ltd. * * This program is free software: you can redistribute it and/or modify it * under the terms of the GNU General Public License version 3, as published * by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranties of * MERCHANTABILITY, SATISFACTORY QUALITY, or FITNESS FOR A PARTICULAR * PURPOSE. See the GNU General Public License for more details. * * You should have received a copy of the GNU General Public License along * with this program. If not, see . * * In addition, as a special exception, the copyright holders give * permission to link the code of portions of this program with the * OpenSSL library under certain conditions as described in each * individual source file, and distribute linked combinations * including the two. * You must obey the GNU General Public License in all respects * for all of the code used other than OpenSSL. If you modify * file(s) with this exception, you may extend this exception to your * version of the file(s), but you are not obligated to do so. If you * do not wish to do so, delete this exception statement from your * version. If you delete this exception statement from all source * files in the program, then also delete it here. */ #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "interface.h" #include "key_file_locator.h" namespace click { const std::unordered_set& nonClickDesktopFiles() { static std::unordered_set set = { "address-book-app.desktop", "camera-app.desktop", "click-update-manager.desktop", "dialer-app.desktop", "friends-app.desktop", "gallery-app.desktop", "mediaplayer-app.desktop", "messaging-app.desktop", "music-app.desktop", "ubuntu-filemanager-app.desktop", "ubuntu-system-settings.desktop", "webbrowser-app.desktop", }; return set; } static const std::string DESKTOP_FILE_GROUP("Desktop Entry"); static const std::string DESKTOP_FILE_KEY_NAME("Name"); static const std::string DESKTOP_FILE_KEY_ICON("Icon"); static const std::string DESKTOP_FILE_KEY_APP_ID("X-Ubuntu-Application-ID"); static const std::string DESKTOP_FILE_UBUNTU_TOUCH("X-Ubuntu-Touch"); static const std::string DESKTOP_FILE_COMMENT("Comment"); static const std::string DESKTOP_FILE_SCREENSHOT("X-Screenshot"); static const std::string DESKTOP_FILE_NODISPLAY("NoDisplay"); static const std::string DESKTOP_FILE_ONLYSHOWIN("OnlyShowIn"); static const std::string ONLYSHOWIN_UNITY("Unity"); Interface::Interface(const QSharedPointer& keyFileLocator) : keyFileLocator(keyFileLocator) { } Interface::~Interface() { } bool Interface::show_desktop_apps() { return getenv(ENV_SHOW_DESKTOP_APPS) != nullptr; } bool Interface::is_visible_app(const unity::util::IniParser &keyFile) { if (keyFile.has_key(DESKTOP_FILE_GROUP, DESKTOP_FILE_NODISPLAY)) { auto val = keyFile.get_string(DESKTOP_FILE_GROUP, DESKTOP_FILE_NODISPLAY); if (val == std::string("true")) { return false; } } if (keyFile.has_key(DESKTOP_FILE_GROUP, DESKTOP_FILE_ONLYSHOWIN)) { auto value = keyFile.get_string(DESKTOP_FILE_GROUP, DESKTOP_FILE_ONLYSHOWIN); std::stringstream ss(value); std::string item; while (std::getline(ss, item, ';')) { if (item == ONLYSHOWIN_UNITY) { return true; } } return false; } return true; } /* find_installed_apps() * * Find all of the installed apps matching @search_query in a timeout. */ std::vector Interface::find_installed_apps(const QString& search_query) { std::vector result; bool include_desktop_results = show_desktop_apps(); std::map installTimes; auto enumerator = [&result, &installTimes, this, search_query, include_desktop_results] (const unity::util::IniParser& keyFile, const std::string& filename) { if (is_visible_app(keyFile) == false) { return; // from the enumerator lambda } if (include_desktop_results || keyFile.has_key(DESKTOP_FILE_GROUP, DESKTOP_FILE_UBUNTU_TOUCH) || keyFile.has_key(DESKTOP_FILE_GROUP, DESKTOP_FILE_KEY_APP_ID) || Interface::is_non_click_app(QString::fromStdString(filename))) { QString name = keyFile.get_string(DESKTOP_FILE_GROUP, DESKTOP_FILE_KEY_NAME).c_str(); if (search_query.isEmpty() || (name != NULL && name.contains(search_query, Qt::CaseInsensitive))) { Application app; struct stat times; installTimes[filename] = stat(filename.c_str(), ×) == 0 ? times.st_mtime : 0; QString app_url = "application:///" + QString::fromStdString(filename); app.url = app_url.toUtf8().data(); app.title = name.toUtf8().data(); if (keyFile.has_key(DESKTOP_FILE_GROUP, DESKTOP_FILE_KEY_ICON)) { app.icon_url = Interface::add_theme_scheme( keyFile.get_string(DESKTOP_FILE_GROUP, DESKTOP_FILE_KEY_ICON)); } if (keyFile.has_key(DESKTOP_FILE_GROUP, DESKTOP_FILE_KEY_APP_ID)) { QString app_id = QString::fromStdString(keyFile.get_string( DESKTOP_FILE_GROUP, DESKTOP_FILE_KEY_APP_ID)); QStringList id = app_id.split("_", QString::SkipEmptyParts); app.name = id[0].toUtf8().data(); app.version = id[2].toUtf8().data(); } else { if (keyFile.has_key(DESKTOP_FILE_GROUP, DESKTOP_FILE_COMMENT)) { app.description = keyFile.get_string(DESKTOP_FILE_GROUP, DESKTOP_FILE_COMMENT); } else { app.description = ""; } if (keyFile.has_key(DESKTOP_FILE_GROUP, DESKTOP_FILE_SCREENSHOT)) { app.main_screenshot = keyFile.get_string(DESKTOP_FILE_GROUP, DESKTOP_FILE_SCREENSHOT); } else { app.main_screenshot = ""; } } result.push_back(app); qDebug() << QString::fromStdString(app.title) << QString::fromStdString(app.icon_url) << QString::fromStdString(filename); } } }; keyFileLocator->enumerateKeyFilesForInstalledApplications(enumerator); // Sort applications so that newest come first. std::sort(result.begin(), result.end(), [&installTimes](const Application& a, const Application& b) {return installTimes[a.name] > installTimes[b.name];}); return result; } /* is_non_click_app() * * Tests that @filename is one of the special-cased filenames for apps * which are not packaged as clicks, but required on Ubuntu Touch. */ bool Interface::is_non_click_app(const QString& filename) { return click::nonClickDesktopFiles().count(filename.toUtf8().data()) > 0; } /* * is_icon_identifier() * * Checks if @filename has no / in it */ bool Interface::is_icon_identifier(const std::string &icon_id) { return icon_id.find("/") == std::string::npos; } /* * add_theme_scheme() * * Adds the theme prefix if the filename is not an icon identifier */ std::string Interface::add_theme_scheme(const std::string& icon_id) { if (is_icon_identifier(icon_id)) { return "image://theme/" + icon_id; } return icon_id; } std::vector FrameworkLocator::list_folder(const std::string& folder, const std::string& pattern) { std::vector result; QDir dir(QString::fromStdString(folder), QString::fromStdString(pattern), QDir::Unsorted, QDir::Readable | QDir::Files); QStringList entries = dir.entryList(); for (int i = 0; i < entries.size(); ++i) { QString filename = entries.at(i); result.push_back(filename.toStdString()); } return result; } std::vector FrameworkLocator::get_available_frameworks() { std::vector result; for (auto f: list_folder(FRAMEWORKS_FOLDER, FRAMEWORKS_PATTERN)) { result.push_back(f.substr(0, f.size()-FRAMEWORKS_EXTENSION_LENGTH)); } return result; } ManifestList manifest_list_from_json(const std::string& json) { using namespace boost::property_tree; std::istringstream is(json); ptree pt; read_json(is, pt); ManifestList manifests; BOOST_FOREACH(ptree::value_type &v, pt) { assert(v.first.empty()); // array elements have no names auto node = v.second; std::string name = node.get("name"); std::string version = node.get("version"); std::string first_app_name; BOOST_FOREACH(ptree::value_type &sv, node.get_child("hooks")) { // FIXME: "primary app" for a package is not defined, we just // use first one here: first_app_name = sv.first; break; } qDebug() << "adding manifest: " << name.c_str() << version.c_str() << first_app_name.c_str(); manifests.push_back(Manifest(name, version, first_app_name)); } return manifests; } Manifest manifest_from_json(const std::string& json) { using namespace boost::property_tree; std::istringstream is(json); ptree pt; read_json(is, pt); std::string name = pt.get("name"); std::string version = pt.get("version"); std::string first_app_name; BOOST_FOREACH(ptree::value_type &sv, pt.get_child("hooks")) { // FIXME: "primary app" for a package is not defined, we just // use first one here: first_app_name = sv.first; break; } qDebug() << "adding manifest: " << name.c_str() << version.c_str() << first_app_name.c_str(); Manifest manifest(name, version, first_app_name); return manifest; } void Interface::get_manifests(std::function callback) { QSharedPointer process(new QProcess()); typedef void(QProcess::*QProcessFinished)(int, QProcess::ExitStatus); typedef void(QProcess::*QProcessError)(QProcess::ProcessError); QObject::connect(process.data(), static_cast(&QProcess::finished), [callback, process](int code, QProcess::ExitStatus /*status*/) { qDebug() << "manifest command finished with exit code:" << code; try { auto data = process.data()->readAllStandardOutput().data(); ManifestList manifests = manifest_list_from_json(data); qDebug() << "calling back "; callback(manifests, ManifestError::NoError); } catch ( ... ) { callback(ManifestList(), ManifestError::ParseError); } } ); QObject::connect(process.data(), static_cast(&QProcess::error), [callback, process](QProcess::ProcessError error) { qCritical() << "error running command:" << error; callback(ManifestList(), ManifestError::CallError); } ); std::string command = "click list --manifest"; qDebug() << "Running command:" << command.c_str(); process->start(command.c_str()); } void Interface::get_manifest_for_app(const std::string &app_id, std::function callback) { QSharedPointer process(new QProcess()); typedef void(QProcess::*QProcessFinished)(int, QProcess::ExitStatus); typedef void(QProcess::*QProcessError)(QProcess::ProcessError); QObject::connect(process.data(), static_cast(&QProcess::finished), [callback, process](int code, QProcess::ExitStatus /*status*/) { qDebug() << "manifest command finished with exit code:" << code; try { auto data = process.data()->readAllStandardOutput().data(); Manifest manifest = manifest_from_json(data); qDebug() << "calling back "; callback(manifest, ManifestError::NoError); } catch ( ... ) { callback(Manifest(), ManifestError::ParseError); } } ); QObject::connect(process.data(), static_cast(&QProcess::error), [callback, process](QProcess::ProcessError error) { qCritical() << "error running command:" << error; callback(Manifest(), ManifestError::CallError); } ); std::string command = "click info " + app_id; qDebug() << "Running command:" << command.c_str(); process->start(command.c_str()); } void Interface::get_dotdesktop_filename(const std::string &app_id, std::function callback) { get_manifest_for_app(app_id, [app_id, callback] (Manifest manifest, ManifestError error) { qDebug() << "in get_dotdesktop_filename callback"; if (error != ManifestError::NoError){ callback(std::string("Internal Error"), error); return; } qDebug() << "in get_dotdesktop_filename callback"; if (!manifest.name.empty()) { std::string ddstr = manifest.name + "_" + manifest.first_app_name + "_" + manifest.version + ".desktop"; callback(ddstr, ManifestError::NoError); } else { qCritical() << "Warning: no manifest found for " << app_id.c_str(); callback(std::string("Not found"), ManifestError::CallError); } }); } } // namespace click unity-scope-click-0.1+14.04.20140417/scope/click/webclient.cpp0000644000015301777760000001330512323764232024200 0ustar pbusernogroup00000000000000/* * Copyright (C) 2014 Canonical Ltd. * * This program is free software: you can redistribute it and/or modify it * under the terms of the GNU General Public License version 3, as published * by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranties of * MERCHANTABILITY, SATISFACTORY QUALITY, or FITNESS FOR A PARTICULAR * PURPOSE. See the GNU General Public License for more details. * * You should have received a copy of the GNU General Public License along * with this program. If not, see . * * In addition, as a special exception, the copyright holders give * permission to link the code of portions of this program with the * OpenSSL library under certain conditions as described in each * individual source file, and distribute linked combinations * including the two. * You must obey the GNU General Public License in all respects * for all of the code used other than OpenSSL. If you modify * file(s) with this exception, you may extend this exception to your * version of the file(s), but you are not obligated to do so. If you * do not wish to do so, delete this exception statement from your * version. If you delete this exception statement from all source * files in the program, then also delete it here. */ #include #include #include #include "webclient.h" #include "smartconnect.h" #include "network_access_manager.h" void click::web::CallParams::add(const std::string& key, const std::string& value) { query.addQueryItem(key.c_str(), value.c_str()); } bool click::web::CallParams::operator==(const CallParams &other) const { return (this->query == other.query); } struct click::web::Client::Private { Private(const QSharedPointer nam, const QSharedPointer sso) : network_access_manager(nam), sso(sso) { } QSharedPointer network_access_manager; QSharedPointer sso; }; click::web::Client::Client( const QSharedPointer& network_access_manager, const QSharedPointer& sso ) : impl(new Private{network_access_manager, sso}) { } click::web::Client::~Client() { } QSharedPointer click::web::Client::call( const std::string& iri, const click::web::CallParams& params) { return call(iri, "GET", false, std::map(), "", params); } QSharedPointer click::web::Client::call( const std::string& iri, const std::string& method, bool sign, const std::map& headers, const std::string& data, const click::web::CallParams& params) { QUrl url(iri.c_str()); url.setQuery(params.query); QNetworkRequest request(url); QSharedPointer buffer(new QBuffer()); buffer->setData(data.c_str(), data.length()); for (const auto& kv : headers) { QByteArray header_name(kv.first.c_str(), kv.first.length()); QByteArray header_value(kv.second.c_str(), kv.second.length()); request.setRawHeader(header_name, header_value); } QSharedPointer responsePtr = QSharedPointer(new click::web::Response(buffer)); auto doConnect = [=, &request]() { QByteArray verb(method.c_str(), method.length()); auto reply = impl->network_access_manager->sendCustomRequest(request, verb, buffer.data()); responsePtr->setReply(reply); }; if (sign) { click::utils::SmartConnect sc(responsePtr.data()); sc.connect(impl->sso.data(), &click::CredentialsService::credentialsFound, [=, &request](const UbuntuOne::Token& token) { QString auth_header = token.signUrl(url.toString(), method.c_str()); request.setRawHeader(AUTHORIZATION.c_str(), auth_header.toUtf8()); doConnect(); }); sc.connect(impl->sso.data(), &click::CredentialsService::credentialsNotFound, []() { // TODO: Need to handle and propagate error conditons. }); // TODO: Need to handle error signal once in CredentialsService. impl->sso->getCredentials(); } else { doConnect(); } return responsePtr; } click::web::Response::Response(const QSharedPointer& buffer, QObject* parent) : QObject(parent), buffer(buffer) { } void click::web::Response::setReply(QSharedPointer reply) { this->reply = reply; auto sc = new click::utils::SmartConnect(reply.data()); sc->connect(this->reply.data(), &click::network::Reply::finished, [this](){replyFinished();}); sc->connect(this->reply.data(), &click::network::Reply::error, [this](QNetworkReply::NetworkError err){errorHandler(err);}); } void click::web::Response::replyFinished() { auto response = reply->readAll(); qDebug() << "got response" << response.toPercentEncoding(" "); emit finished(response); } void click::web::Response::errorHandler(QNetworkReply::NetworkError network_error) { auto message = reply->errorString() + QString(" (%1)").arg(network_error); qDebug() << "emitting error: " << message; emit error(message); } void click::web::Response::abort() { reply->abort(); } click::web::Response::~Response() { } unity-scope-click-0.1+14.04.20140417/scope/click/key_file_locator.h0000644000015301777760000000461412323764232025206 0ustar pbusernogroup00000000000000/* * Copyright (C) 2014 Canonical Ltd. * * This program is free software: you can redistribute it and/or modify it * under the terms of the GNU General Public License version 3, as published * by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranties of * MERCHANTABILITY, SATISFACTORY QUALITY, or FITNESS FOR A PARTICULAR * PURPOSE. See the GNU General Public License for more details. * * You should have received a copy of the GNU General Public License along * with this program. If not, see . * * In addition, as a special exception, the copyright holders give * permission to link the code of portions of this program with the * OpenSSL library under certain conditions as described in each * individual source file, and distribute linked combinations * including the two. * You must obey the GNU General Public License in all respects * for all of the code used other than OpenSSL. If you modify * file(s) with this exception, you may extend this exception to your * version of the file(s), but you are not obligated to do so. If you * do not wish to do so, delete this exception statement from your * version. If you delete this exception statement from all source * files in the program, then also delete it here. */ #ifndef CLICK_KEY_FILE_LOCATOR_H #define CLICK_KEY_FILE_LOCATOR_H #include #include namespace unity { namespace util { class IniParser; } } namespace click { class KeyFileLocator { public: static const std::string& systemApplicationsDirectory(); static const std::string& userApplicationsDirectory(); typedef std::function Enumerator; KeyFileLocator(const std::string& systemApplicationsDir = systemApplicationsDirectory(), const std::string& userApplicationsDir = userApplicationsDirectory()); KeyFileLocator(const KeyFileLocator&) = delete; virtual ~KeyFileLocator() = default; KeyFileLocator& operator=(const KeyFileLocator&) = delete; bool operator==(const KeyFileLocator&) const = delete; virtual void enumerateKeyFilesForInstalledApplications(const Enumerator& enumerator); private: std::string systemApplicationsDir; std::string userApplicationsDir; }; } #endif // CLICK_KEY_FILE_LOCATOR_H unity-scope-click-0.1+14.04.20140417/scope/click/download-manager.cpp0000644000015301777760000003046712323764232025453 0ustar pbusernogroup00000000000000/* * Copyright (C) 2014 Canonical Ltd. * * This program is free software: you can redistribute it and/or modify it * under the terms of the GNU General Public License version 3, as published * by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranties of * MERCHANTABILITY, SATISFACTORY QUALITY, or FITNESS FOR A PARTICULAR * PURPOSE. See the GNU General Public License for more details. * * You should have received a copy of the GNU General Public License along * with this program. If not, see . * * In addition, as a special exception, the copyright holders give * permission to link the code of portions of this program with the * OpenSSL library under certain conditions as described in each * individual source file, and distribute linked combinations * including the two. * You must obey the GNU General Public License in all respects * for all of the code used other than OpenSSL. If you modify * file(s) with this exception, you may extend this exception to your * version of the file(s), but you are not obligated to do so. If you * do not wish to do so, delete this exception statement from your * version. If you delete this exception statement from all source * files in the program, then also delete it here. */ #include "download-manager.h" #include #include #include #include #include #include "qtbridge.h" namespace u1 = UbuntuOne; #include #include namespace udm = Ubuntu::DownloadManager; #include #include #include namespace { static const QString DOWNLOAD_APP_ID_KEY = "app_id"; static const QString DOWNLOAD_COMMAND_KEY = "post-download-command"; // Pass two commands using sh -c because UDM expects a single // executable. // Use a single string for both commands because the // list is eventually given to QProcess, which adds quotes around each // argument, while -c expects a single quoted string. // Then, use the $0 positional variable in the pkcon command to let us // pass "$file" as a separate list element, which UDM requires for // substitution. static const QString DOWNLOAD_COMMAND = QStringLiteral("pkcon -p install-local ") % click::QUOTED_ARG0 % QStringLiteral(" && ") % click::DBUSSEND_COMMAND; static const QVariant DOWNLOAD_CMDLINE = QVariant(QStringList() << "/bin/sh" << "-c" << DOWNLOAD_COMMAND << "$file"); static const QString DOWNLOAD_MANAGER_DO_NOT_HASH = ""; static const QString DOWNLOAD_MANAGER_IGNORE_HASH_ALGORITHM = ""; } struct click::DownloadManager::Private { Private(const QSharedPointer& networkAccessManager, const QSharedPointer& credentialsService, const QSharedPointer& systemDownloadManager) : nam(networkAccessManager), credentialsService(credentialsService), systemDownloadManager(systemDownloadManager) { } void updateCredentialsFromService() { credentialsService->getCredentials(); } QSharedPointer nam; QSharedPointer credentialsService; QSharedPointer systemDownloadManager; QSharedPointer reply; QString downloadUrl; QString appId; }; const QByteArray& click::CLICK_TOKEN_HEADER() { static const QByteArray result("X-Click-Token"); return result; } click::DownloadManager::DownloadManager(const QSharedPointer& networkAccessManager, const QSharedPointer& credentialsService, const QSharedPointer& systemDownloadManager, QObject *parent) : QObject(parent), impl(new Private(networkAccessManager, credentialsService, systemDownloadManager)) { QMetaObject::Connection c = connect(impl->credentialsService.data(), &click::CredentialsService::credentialsFound, this, &click::DownloadManager::handleCredentialsFound); if (!c) { qDebug() << "failed to connect to credentialsFound"; } c = connect(impl->credentialsService.data(), &click::CredentialsService::credentialsNotFound, this, &click::DownloadManager::handleCredentialsNotFound); if (!c) { qDebug() << "failed to connect to credentialsNotFound"; } // NOTE: using SIGNAL/SLOT macros here because new-style // connections are flaky on ARM. c = connect(impl->systemDownloadManager.data(), SIGNAL(downloadCreated(Download*)), this, SLOT(handleDownloadCreated(Download*))); if (!c) { qDebug() << "failed to connect to systemDownloadManager::downloadCreated"; } } click::DownloadManager::~DownloadManager(){ } void click::DownloadManager::startDownload(const QString& downloadUrl, const QString& appId) { impl->appId = appId; // NOTE: using SIGNAL/SLOT macros here because new-style // connections are flaky on ARM. QObject::connect(this, SIGNAL(clickTokenFetched(QString)), this, SLOT(handleClickTokenFetched(QString)), Qt::UniqueConnection); QObject::connect(this, SIGNAL(clickTokenFetchError(QString)), this, SLOT(handleClickTokenFetchError(QString)), Qt::UniqueConnection); fetchClickToken(downloadUrl); } void click::DownloadManager::handleClickTokenFetched(const QString& clickToken) { QVariantMap metadata; metadata[DOWNLOAD_COMMAND_KEY] = DOWNLOAD_CMDLINE; metadata[DOWNLOAD_APP_ID_KEY] = impl->appId; QMap headers; headers[CLICK_TOKEN_HEADER()] = clickToken; udm::DownloadStruct downloadStruct(impl->downloadUrl, DOWNLOAD_MANAGER_DO_NOT_HASH, DOWNLOAD_MANAGER_IGNORE_HASH_ALGORITHM, metadata, headers); impl->systemDownloadManager->createDownload(downloadStruct); } void click::DownloadManager::handleClickTokenFetchError(const QString& errorMessage) { emit downloadError(errorMessage); } void click::DownloadManager::handleDownloadCreated(udm::Download *download) { if (download->isError()) { QString error = download->error()->errorString(); qDebug() << "Received error from ubuntu-download-manager:" << error; emit downloadError(error); } else { download->start(); emit downloadStarted(download->id()); } } void click::DownloadManager::fetchClickToken(const QString& downloadUrl) { impl->downloadUrl = downloadUrl; impl->updateCredentialsFromService(); } void click::DownloadManager::handleCredentialsFound(const u1::Token &token) { qDebug() << "Credentials found, signing url " << impl->downloadUrl; QString authHeader = token.signUrl(impl->downloadUrl, QStringLiteral("HEAD")); QNetworkRequest req; req.setRawHeader(QStringLiteral("Authorization").toUtf8(), authHeader.toUtf8()); req.setUrl(impl->downloadUrl); impl->reply = impl->nam->head(req); // NOTE: using SIGNAL/SLOT macros here because new-style // connections are flaky on ARM. QObject::connect(impl->reply.data(), SIGNAL(error(QNetworkReply::NetworkError)), this, SLOT(handleNetworkError(QNetworkReply::NetworkError))); QObject::connect(impl->reply.data(), SIGNAL(finished()), this, SLOT(handleNetworkFinished())); } void click::DownloadManager::handleCredentialsNotFound() { qDebug() << "No credentials were found."; emit credentialsNotFound(); } void click::DownloadManager::handleNetworkFinished() { QVariant statusAttr = impl->reply->attribute(QNetworkRequest::HttpStatusCodeAttribute); if(!statusAttr.isValid()) { QString msg("Invalid HTTP response."); qDebug() << msg; emit clickTokenFetchError(msg); return; } int status = statusAttr.toInt(); if (status != 200){ qDebug() << impl->reply->rawHeaderPairs(); qDebug() << impl->reply->readAll(); QString msg = QString("HTTP status not OK: %1").arg(status); emit clickTokenFetchError(msg); return; } if(!impl->reply->hasRawHeader(CLICK_TOKEN_HEADER())) { QString msg = "Response does not contain Click Header"; qDebug() << msg << "Full response:"; qDebug() << impl->reply->rawHeaderPairs(); qDebug() << impl->reply->readAll(); emit clickTokenFetchError(msg); return; } QString clickTokenHeaderStr = impl->reply->rawHeader(CLICK_TOKEN_HEADER()); impl->reply.reset(); emit clickTokenFetched(clickTokenHeaderStr); } void click::DownloadManager::handleNetworkError(QNetworkReply::NetworkError error) { qDebug() << "error in network request for click token: " << error << impl->reply->errorString(); impl->reply.reset(); emit clickTokenFetchError(QString("Network Error")); } // Downloader namespace { click::DownloadManager& downloadManagerInstance( const QSharedPointer& networkAccessManager) { static QSharedPointer ssoService { new click::CredentialsService() }; static QSharedPointer udm { Ubuntu::DownloadManager::Manager::createSessionManager() }; static click::DownloadManager instance(networkAccessManager, ssoService, udm); return instance; } } click::Downloader::Downloader(const QSharedPointer& networkAccessManager) : networkAccessManager(networkAccessManager) { } void click::Downloader::get_download_progress(std::string /*package_name*/, const std::function& /*callback*/) { // TODO, unimplemented. see https://bugs.launchpad.net/ubuntu-download-manager/+bug/1277814 } namespace { class Callback : public QObject { Q_OBJECT public: Callback(const std::function)>& cb) : cb(cb) { } public slots: void onDownloadStarted(const QString& downloadId) { cb(std::make_pair(downloadId.toUtf8().data(), click::InstallError::NoError)); // We shouldn't do this, but: We have no other indication whether a download finished or not. // TODO(tvoss): Remove as soon as a donwload finished signal is available. deleteLater(); } void onDownloadError(const QString& errorMessage) { cb(std::make_pair(errorMessage.toStdString(), click::InstallError::DownloadInstallError)); deleteLater(); } void onCredentialsError() { cb(std::make_pair(std::string(), click::InstallError::CredentialsError)); deleteLater(); } private: std::function)> cb; }; } void click::Downloader::startDownload(std::string url, std::string package_name, const std::function)>& callback) { qt::core::world::enter_with_task([this, callback, url, package_name] (qt::core::world::Environment& /*env*/) { auto& dm = downloadManagerInstance(networkAccessManager); // Leverage automatic lifetime mgmt for QObjects here. auto cb = new Callback{callback}; QObject::connect(&dm, &click::DownloadManager::downloadStarted, cb, &Callback::onDownloadStarted); QObject::connect(&dm, &click::DownloadManager::credentialsNotFound, cb, &Callback::onCredentialsError); QObject::connect(&dm, &click::DownloadManager::downloadError, cb, &Callback::onDownloadError); dm.startDownload(QString::fromStdString(url), QString::fromStdString(package_name)); }); } #include "download-manager.moc" unity-scope-click-0.1+14.04.20140417/scope/click/CMakeLists.txt0000644000015301777760000000214012323764232024253 0ustar pbusernogroup00000000000000SET (CMAKE_INCLUDE_CURRENT_DIR ON) SET (CMAKE_AUTOMOC ON) find_package (Qt5Core REQUIRED) pkg_check_modules(JSON_CPP REQUIRED jsoncpp) pkg_check_modules(LIBURL_DISPATCHER REQUIRED url-dispatcher-1) add_definitions( -DGETTEXT_PACKAGE=\"${PROJECT_NAME}\" -DGETTEXT_LOCALEDIR=\"${CMAKE_INSTALL_LOCALEDIR}\" ) add_library(${SCOPE_LIB_UNVERSIONED} SHARED download-manager.cpp index.cpp interface.cpp key_file_locator.cpp network_access_manager.cpp query.cpp reviews.cpp scope.cpp smartconnect.cpp preview.cpp ubuntuone_credentials.cpp webclient.cpp qtbridge.cpp ) include_directories( ${JSON_CPP_INCLUDE_DIRS} ${LIBURL_DISPATCHER_INCLUDE_DIRS}) link_directories(${LIBURL_DISPATCHER_LIBRARY_DIRS}) qt5_use_modules (${SCOPE_LIB_UNVERSIONED} Network) target_link_libraries (${SCOPE_LIB_UNVERSIONED} ${JSON_CPP_LDFLAGS} ${UNITY_SCOPES_LDFLAGS} ${UBUNTUONE_LDFLAGS} ${UBUNTU_DOWNLOAD_MANAGER_CLIENT_LDFLAGS} ${UBUNTU_DOWNLOAD_MANAGER_COMMON_LDFLAGS} ${LIBURL_DISPATCHER_LIBRARIES} ) install( TARGETS ${SCOPE_LIB_UNVERSIONED} LIBRARY DESTINATION "${SCOPE_LIB_DIR}" ) unity-scope-click-0.1+14.04.20140417/scope/click/query.h0000644000015301777760000000513412323764232023037 0ustar pbusernogroup00000000000000/* * Copyright (C) 2014 Canonical Ltd. * * This program is free software: you can redistribute it and/or modify it * under the terms of the GNU General Public License version 3, as published * by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranties of * MERCHANTABILITY, SATISFACTORY QUALITY, or FITNESS FOR A PARTICULAR * PURPOSE. See the GNU General Public License for more details. * * You should have received a copy of the GNU General Public License along * with this program. If not, see . * * In addition, as a special exception, the copyright holders give * permission to link the code of portions of this program with the * OpenSSL library under certain conditions as described in each * individual source file, and distribute linked combinations * including the two. * You must obey the GNU General Public License in all respects * for all of the code used other than OpenSSL. If you modify * file(s) with this exception, you may extend this exception to your * version of the file(s), but you are not obligated to do so. If you * do not wish to do so, delete this exception statement from your * version. If you delete this exception statement from all source * files in the program, then also delete it here. */ #ifndef CLICK_QUERY_H #define CLICK_QUERY_H #include namespace scopes = unity::scopes; #include namespace click { class Query : public scopes::SearchQueryBase { public: struct JsonKeys { JsonKeys() = delete; constexpr static const char* RESOURCE_URL{"resource_url"}; constexpr static const char* TITLE{"title"}; constexpr static const char* ICON_URL{"icon_url"}; constexpr static const char* NAME{"name"}; }; struct ResultKeys { ResultKeys() = delete; constexpr static const char* NAME{"name"}; constexpr static const char* DESCRIPTION{"description"}; constexpr static const char* MAIN_SCREENSHOT{"main_screenshot"}; constexpr static const char* INSTALLED{"installed"}; constexpr static const char* DOWNLOAD_URL{"download_url"}; constexpr static const char* VERSION{"version"}; }; Query(std::string const& query, scopes::SearchMetadata const& metadata); ~Query(); virtual void cancelled() override; virtual void run(scopes::SearchReplyProxy const& reply) override; private: struct Private; QSharedPointer impl; }; } #endif // CLICK_QUERY_H unity-scope-click-0.1+14.04.20140417/scope/click/reviews.h0000644000015301777760000000552412323764232023361 0ustar pbusernogroup00000000000000/* * Copyright (C) 2014 Canonical Ltd. * * This program is free software: you can redistribute it and/or modify it * under the terms of the GNU General Public License version 3, as published * by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranties of * MERCHANTABILITY, SATISFACTORY QUALITY, or FITNESS FOR A PARTICULAR * PURPOSE. See the GNU General Public License for more details. * * You should have received a copy of the GNU General Public License along * with this program. If not, see . * * In addition, as a special exception, the copyright holders give * permission to link the code of portions of this program with the * OpenSSL library under certain conditions as described in each * individual source file, and distribute linked combinations * including the two. * You must obey the GNU General Public License in all respects * for all of the code used other than OpenSSL. If you modify * file(s) with this exception, you may extend this exception to your * version of the file(s), but you are not obligated to do so. If you * do not wish to do so, delete this exception statement from your * version. If you delete this exception statement from all source * files in the program, then also delete it here. */ #ifndef CLICK_REVIEWS_H #define CLICK_REVIEWS_H #include #include #include #include "index.h" #include "webclient.h" namespace click { const std::string REVIEWS_BASE_URL_ENVVAR = "CLICK_REVIEWS_BASE_URL"; const std::string REVIEWS_BASE_URL = "https://reviews.ubuntu.com"; const std::string REVIEWS_API_PATH = "/click/api/1.0/reviews/"; const std::string REVIEWS_QUERY_ARGNAME = "package_name"; struct Review { uint32_t id; int rating; uint32_t usefulness_favorable; uint32_t usefulness_total; bool hide; std::string date_created; std::string date_deleted; std::string package_name; std::string package_version; std::string language; std::string summary; std::string review_text; std::string reviewer_name; std::string reviewer_username; }; typedef std::vector ReviewList; ReviewList review_list_from_json (const std::string& json); class Reviews { protected: QSharedPointer client; public: enum class Error {NoError, CredentialsError, NetworkError}; Reviews(const QSharedPointer& client); virtual ~Reviews(); click::web::Cancellable fetch_reviews (const std::string& package_name, std::function callback); static std::string get_base_url (); }; bool operator==(const Review& lhs, const Review& rhs); } // namespace click #endif // CLICK_REVIEWS_H unity-scope-click-0.1+14.04.20140417/scope/click/preview.h0000644000015301777760000001555412323764232023362 0ustar pbusernogroup00000000000000/* * Copyright (C) 2014 Canonical Ltd. * * This program is free software: you can redistribute it and/or modify it * under the terms of the GNU General Public License version 3, as published * by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranties of * MERCHANTABILITY, SATISFACTORY QUALITY, or FITNESS FOR A PARTICULAR * PURPOSE. See the GNU General Public License for more details. * * You should have received a copy of the GNU General Public License along * with this program. If not, see . * * In addition, as a special exception, the copyright holders give * permission to link the code of portions of this program with the * OpenSSL library under certain conditions as described in each * individual source file, and distribute linked combinations * including the two. * You must obey the GNU General Public License in all respects * for all of the code used other than OpenSSL. If you modify * file(s) with this exception, you may extend this exception to your * version of the file(s), but you are not obligated to do so. If you * do not wish to do so, delete this exception statement from your * version. If you delete this exception statement from all source * files in the program, then also delete it here. */ #ifndef CLICKPREVIEW_H #define CLICKPREVIEW_H #include "index.h" #include "network_access_manager.h" #include "download-manager.h" #include "qtbridge.h" #include "reviews.h" #include #include #include namespace scopes = unity::scopes; namespace click { class Preview : public unity::scopes::PreviewQueryBase { public: struct Actions { Actions() = delete; constexpr static const char* INSTALL_CLICK{"install_click"}; constexpr static const char* BUY_CLICK{"buy_click"}; constexpr static const char* DOWNLOAD_COMPLETED{"finished"}; constexpr static const char* DOWNLOAD_FAILED{"failed"}; constexpr static const char* PURCHASE_SUCCEEDED{"purchase_succeeded"}; constexpr static const char* PURCHASE_FAILED{"purchase_failed"}; constexpr static const char* OPEN_CLICK{"open_click"}; constexpr static const char* PIN_TO_LAUNCHER{"pin_to_launcher"}; constexpr static const char* UNINSTALL_CLICK{"uninstall_click"}; constexpr static const char* CONFIRM_UNINSTALL{"confirm_uninstall"}; constexpr static const char* CLOSE_PREVIEW{"close_preview"}; constexpr static const char* OPEN_ACCOUNTS{"open_accounts"}; }; Preview(const unity::scopes::Result& result); Preview(const unity::scopes::Result& result, const QSharedPointer& client); virtual ~Preview(); // From unity::scopes::PreviewQuery void cancelled() override; virtual void run(unity::scopes::PreviewReplyProxy const& reply) override = 0; protected: virtual void populateDetails(std::function details_callback, std::function reviews_callback); virtual scopes::PreviewWidgetList headerWidgets(const PackageDetails &details); virtual scopes::PreviewWidgetList descriptionWidgets(const PackageDetails &details); virtual scopes::PreviewWidgetList reviewsWidgets(const click::ReviewList &reviewlist); virtual scopes::PreviewWidgetList downloadErrorWidgets(); virtual scopes::PreviewWidgetList loginErrorWidgets(); virtual scopes::PreviewWidgetList errorWidgets(const scopes::Variant& title, const scopes::Variant& subtitle, const scopes::Variant& action_id, const scopes::Variant& action_label); scopes::Result result; QSharedPointer index; click::web::Cancellable index_operation; QSharedPointer reviews; click::web::Cancellable reviews_operation; }; class DownloadErrorPreview : public Preview { public: DownloadErrorPreview(const unity::scopes::Result& result); virtual ~DownloadErrorPreview(); void run(unity::scopes::PreviewReplyProxy const& reply) override; }; class InstallingPreview : public Preview { public: InstallingPreview(std::string const& download_url, const unity::scopes::Result& result, const QSharedPointer& client, const QSharedPointer& nam); virtual ~InstallingPreview(); void run(unity::scopes::PreviewReplyProxy const& reply) override; protected: virtual scopes::PreviewWidgetList progressBarWidget(const std::string& object_path); std::string download_url; QSharedPointer downloader; }; class InstalledPreview : public Preview { public: InstalledPreview(const unity::scopes::Result& result, const QSharedPointer& client); virtual ~InstalledPreview(); void run(unity::scopes::PreviewReplyProxy const& reply) override; protected: virtual scopes::PreviewWidgetList installedActionButtonWidgets(); }; class PurchasingPreview : public Preview { public: PurchasingPreview(const unity::scopes::Result& result, const QSharedPointer& client); virtual ~PurchasingPreview(); void run(unity::scopes::PreviewReplyProxy const& reply) override; protected: virtual scopes::PreviewWidgetList purchasingWidgets(const PackageDetails &); }; class UninstallConfirmationPreview : public Preview { public: UninstallConfirmationPreview(const unity::scopes::Result& result); virtual ~UninstallConfirmationPreview(); void run(unity::scopes::PreviewReplyProxy const& reply) override; }; class UninstalledPreview : public Preview { public: UninstalledPreview(const unity::scopes::Result& result, const QSharedPointer& client); virtual ~UninstalledPreview(); void run(unity::scopes::PreviewReplyProxy const& reply) override; protected: virtual scopes::PreviewWidgetList uninstalledActionButtonWidgets(const PackageDetails &details); }; // TODO: this is only necessary to perform uninstall. // That should be moved to a separate action, and this class removed. class UninstallingPreview : public UninstalledPreview { public: UninstallingPreview(const unity::scopes::Result& result, const QSharedPointer& client); virtual ~UninstallingPreview(); void run(unity::scopes::PreviewReplyProxy const& reply) override; protected: void uninstall(); }; } // namespace click #endif unity-scope-click-0.1+14.04.20140417/scope/click/index.h0000644000015301777760000001275712323764232023012 0ustar pbusernogroup00000000000000/* * Copyright (C) 2014 Canonical Ltd. * * This program is free software: you can redistribute it and/or modify it * under the terms of the GNU General Public License version 3, as published * by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranties of * MERCHANTABILITY, SATISFACTORY QUALITY, or FITNESS FOR A PARTICULAR * PURPOSE. See the GNU General Public License for more details. * * You should have received a copy of the GNU General Public License along * with this program. If not, see . * * In addition, as a special exception, the copyright holders give * permission to link the code of portions of this program with the * OpenSSL library under certain conditions as described in each * individual source file, and distribute linked combinations * including the two. * You must obey the GNU General Public License in all respects * for all of the code used other than OpenSSL. If you modify * file(s) with this exception, you may extend this exception to your * version of the file(s), but you are not obligated to do so. If you * do not wish to do so, delete this exception statement from your * version. If you delete this exception statement from all source * files in the program, then also delete it here. */ #ifndef CLICKINDEX_H #define CLICKINDEX_H #include #include #include #include #include #include "webclient.h" namespace json = Json; namespace click { const std::string SEARCH_BASE_URL = "https://search.apps.ubuntu.com/"; const std::string SEARCH_PATH = "api/v1/search"; const std::string SUPPORTED_FRAMEWORKS = "framework:ubuntu-sdk-13.10"; const std::string QUERY_ARGNAME = "q"; const std::string ARCHITECTURE = "architecture:"; const std::string DETAILS_PATH = "api/v1/package/"; struct Package { struct JsonKeys { JsonKeys() = delete; constexpr static const char* name{"name"}; constexpr static const char* title{"title"}; constexpr static const char* price{"price"}; constexpr static const char* icon_url{"icon_url"}; constexpr static const char* resource_url{"resource_url"}; }; Package() = default; Package(std::string name, std::string title, double price, std::string icon_url, std::string url) : name(name), title(title), price(price), icon_url(icon_url), url(url) { } Package(std::string name, std::string title, double price, std::string icon_url, std::string url, std::string version) : name(name), title(title), price(price), icon_url(icon_url), url(url), version(version) { } virtual ~Package() = default; std::string name; // formerly app_id std::string title; double price; std::string icon_url; std::string url; std::string version; void matches (std::string query, std::function callback); }; class PackageManager { public: void uninstall (const Package& package, std::function); virtual void execute_uninstall_command (const std::string& command, std::function); }; typedef std::list PackageList; PackageList package_list_from_json(const std::string& json); struct PackageDetails { struct JsonKeys { JsonKeys() = delete; constexpr static const char* name{"name"}; constexpr static const char* title{"title"}; constexpr static const char* icon_url{"icon_url"}; constexpr static const char* description{"description"}; constexpr static const char* download_url{"download_url"}; constexpr static const char* rating{"rating"}; constexpr static const char* keywords{"keywords"}; constexpr static const char* terms_of_service{"terms_of_service"}; constexpr static const char* license{"license"}; constexpr static const char* publisher{"publisher"}; constexpr static const char* main_screenshot_url{"screenshot_url"}; constexpr static const char* more_screenshot_urls{"screenshot_urls"}; constexpr static const char* binary_filesize{"binary_filesize"}; constexpr static const char* version{"version"}; constexpr static const char* framework{"framework"}; }; static PackageDetails from_json(const std::string &json); Package package; std::string description; std::string download_url; double rating; std::string keywords; std::string terms_of_service; std::string license; std::string publisher; std::string main_screenshot_url; std::list more_screenshots_urls; json::Value::UInt64 binary_filesize; std::string version; std::string framework; }; std::ostream& operator<<(std::ostream& out, const PackageDetails& details); class Index { protected: QSharedPointer client; public: enum class Error {NoError, CredentialsError, NetworkError}; Index(const QSharedPointer& client); click::web::Cancellable search (const std::string& query, std::function callback); click::web::Cancellable get_details(const std::string& package_name, std::function callback); ~Index(); }; bool operator==(const Package& lhs, const Package& rhs); bool operator==(const PackageDetails& lhs, const PackageDetails& rhs); } // namespace click #endif // CLICKINDEX_H unity-scope-click-0.1+14.04.20140417/scope/click/network_access_manager.h0000644000015301777760000000612512323764232026377 0ustar pbusernogroup00000000000000/* * Copyright (C) 2014 Canonical Ltd. * * This program is free software: you can redistribute it and/or modify it * under the terms of the GNU General Public License version 3, as published * by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranties of * MERCHANTABILITY, SATISFACTORY QUALITY, or FITNESS FOR A PARTICULAR * PURPOSE. See the GNU General Public License for more details. * * You should have received a copy of the GNU General Public License along * with this program. If not, see . * * In addition, as a special exception, the copyright holders give * permission to link the code of portions of this program with the * OpenSSL library under certain conditions as described in each * individual source file, and distribute linked combinations * including the two. * You must obey the GNU General Public License in all respects * for all of the code used other than OpenSSL. If you modify * file(s) with this exception, you may extend this exception to your * version of the file(s), but you are not obligated to do so. If you * do not wish to do so, delete this exception statement from your * version. If you delete this exception statement from all source * files in the program, then also delete it here. */ #ifndef CLICK_NETWORK_ACCESS_MANAGER_H #define CLICK_NETWORK_ACCESS_MANAGER_H #include #include #include #include #include #include class QByteArray; namespace click { namespace network { // We wrap a QNetworkReply to make sure that we can mock it out // in unit- and integration testing. class Reply : public QObject { Q_OBJECT public: // A Reply instance takes over ownership of the underlying QNetworkReply. explicit Reply(QNetworkReply* reply); Reply(const Reply&) = delete; virtual ~Reply(); Reply& operator=(const Reply&) = delete; virtual void abort(); virtual QByteArray readAll(); virtual QVariant attribute(QNetworkRequest::Attribute code); virtual bool hasRawHeader(const QByteArray &headerName); virtual QString rawHeader(const QByteArray &headerName); virtual QList> rawHeaderPairs(); virtual QString errorString(); signals: void finished(); void error(QNetworkReply::NetworkError); protected: Reply(); private: QScopedPointer reply; }; class AccessManager { public: AccessManager() = default; AccessManager(const AccessManager&) = delete; virtual ~AccessManager() = default; AccessManager& operator=(const AccessManager&) = delete; virtual QSharedPointer get(QNetworkRequest& request); virtual QSharedPointer head(QNetworkRequest& request); virtual QSharedPointer post(QNetworkRequest& request, QByteArray& data); virtual QSharedPointer sendCustomRequest(QNetworkRequest& request, QByteArray& verb, QIODevice *data = 0); }; } } #endif // CLICK_NETWORK_ACCESS_MANAGER_H unity-scope-click-0.1+14.04.20140417/scope/click/interface.h0000644000015301777760000000744212323764232023636 0ustar pbusernogroup00000000000000/* * Copyright (C) 2014 Canonical Ltd. * * This program is free software: you can redistribute it and/or modify it * under the terms of the GNU General Public License version 3, as published * by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranties of * MERCHANTABILITY, SATISFACTORY QUALITY, or FITNESS FOR A PARTICULAR * PURPOSE. See the GNU General Public License for more details. * * You should have received a copy of the GNU General Public License along * with this program. If not, see . * * In addition, as a special exception, the copyright holders give * permission to link the code of portions of this program with the * OpenSSL library under certain conditions as described in each * individual source file, and distribute linked combinations * including the two. * You must obey the GNU General Public License in all respects * for all of the code used other than OpenSSL. If you modify * file(s) with this exception, you may extend this exception to your * version of the file(s), but you are not obligated to do so. If you * do not wish to do so, delete this exception statement from your * version. If you delete this exception statement from all source * files in the program, then also delete it here. */ #ifndef CLICK_INTERFACE_H #define CLICK_INTERFACE_H #include #include #include #include #include #include "application.h" namespace click { class KeyFileLocator; // Hash map of desktop files that are not yet click packages const std::unordered_set& nonClickDesktopFiles(); struct Manifest { Manifest() = default; Manifest(std::string name, std::string version, std::string first_app_name) : name(name), version(version), first_app_name(first_app_name) { } virtual ~Manifest() = default; std::string name; std::string version; std::string first_app_name; }; enum class ManifestError {NoError, CallError, ParseError}; typedef std::list ManifestList; ManifestList manifest_list_from_json(const std::string& json); Manifest manifest_from_json(const std::string& json); class Interface { public: Interface(const QSharedPointer& keyFileLocator); virtual ~Interface(); virtual std::vector find_installed_apps(const QString& search_query); static bool is_non_click_app(const QString& filename); static bool is_icon_identifier(const std::string &icon_id); static std::string add_theme_scheme(const std::string &filename); static void get_manifests(std::function callback); static void get_manifest_for_app(const std::string &app_id, std::function callback); static void get_dotdesktop_filename(const std::string &app_id, std::function callback); constexpr static const char* ENV_SHOW_DESKTOP_APPS {"CLICK_SCOPE_SHOW_DESKTOP_APPS"}; virtual bool is_visible_app(const unity::util::IniParser& keyFile); virtual bool show_desktop_apps(); private: QSharedPointer keyFileLocator; }; class FrameworkLocator { public: constexpr static const char* FRAMEWORKS_FOLDER {"/usr/share/click/frameworks/"}; constexpr static const char* FRAMEWORKS_PATTERN {"*.framework"}; constexpr static const int FRAMEWORKS_EXTENSION_LENGTH = 10; // strlen(".framework") virtual std::vector get_available_frameworks(); protected: virtual std::vector list_folder(const std::string &folder, const std::string &pattern); }; } // namespace click #endif // CLICK_INTERFACE_H unity-scope-click-0.1+14.04.20140417/scope/CMakeLists.txt0000644000015301777760000000005712323764232023173 0ustar pbusernogroup00000000000000add_subdirectory(click) add_subdirectory(tests)unity-scope-click-0.1+14.04.20140417/po/0000755000015301777760000000000012323764741017743 5ustar pbusernogroup00000000000000unity-scope-click-0.1+14.04.20140417/po/genpotfiles.sh0000755000015301777760000000016712323764232022620 0ustar pbusernogroup00000000000000#!/bin/sh sed '/^#/d s/^[[].*] *// /^[ ]*$/d' \ "`dirname ${0}`/POTFILES.in" | sed '$!s/$/ \\/' > POTFILES unity-scope-click-0.1+14.04.20140417/po/LINGUAS0000644000015301777760000000000012323764232020751 0ustar pbusernogroup00000000000000unity-scope-click-0.1+14.04.20140417/po/CMakeLists.txt0000644000015301777760000000235312323764232022501 0ustar pbusernogroup00000000000000include(FindGettext) find_program(GETTEXT_XGETTEXT_EXECUTABLE xgettext) find_program(INTLTOOL_UPDATE intltool-update) set(GETTEXT_PACKAGE ${PROJECT_NAME}) set(POT_FILE ${GETTEXT_PACKAGE}.pot) execute_process( COMMAND cat ${CMAKE_CURRENT_SOURCE_DIR}/LINGUAS OUTPUT_VARIABLE LINGUAS OUTPUT_STRIP_TRAILING_WHITESPACE ) # Creates POTFILES add_custom_target(POTFILES ALL COMMENT "Generating POTFILES" COMMAND ${CMAKE_CURRENT_SOURCE_DIR}/genpotfiles.sh ${CMAKE_SOURCE_DIR} ) # Creates the .pot file containing the translations template add_custom_target(${POT_FILE} ALL COMMENT "Generating translation template" COMMAND XGETTEXT="${GETTEXT_XGETTEXT_EXECUTABLE}" srcdir="${CMAKE_CURRENT_SOURCE_DIR}" ${INTLTOOL_UPDATE} --gettext-package ${GETTEXT_PACKAGE} --pot DEPENDS POTFILES ) # Builds the binary translations catalog for each language # it finds source translations (*.po) for foreach(LANG ${LINGUAS}) list(APPEND PO_FILES "${LANG}.po") gettext_process_po_files(${LANG} ALL PO_FILES "${LANG}.po") set(INSTALL_DIR ${CMAKE_INSTALL_LOCALEDIR}/${LANG}/LC_MESSAGES) install(FILES ${CMAKE_CURRENT_BINARY_DIR}/${LANG}.gmo DESTINATION ${INSTALL_DIR} RENAME ${DOMAIN}.mo ) endforeach(LANG) unity-scope-click-0.1+14.04.20140417/po/POTFILES.in0000644000015301777760000000003012323764232021504 0ustar pbusernogroup00000000000000scope/click/preview.cpp unity-scope-click-0.1+14.04.20140417/CMakeLists.txt0000644000015301777760000000341412323764232022062 0ustar pbusernogroup00000000000000project(unity-scope-click) cmake_minimum_required(VERSION 2.8.10) set(SCOPE_CLICK_VERSION_MAJOR 0) set(SCOPE_CLICK_VERSION_MINOR 0) set(SCOPE_CLICK_VERSION_PATCH 1) # Some default CFLAGS SET (CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -O2 -g -Wall -Wextra -Werror -fPIC") SET (CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -std=c++11 -O2 -g -Wextra -Wall -Werror -fPIC") include(GNUInstallDirs) set(SCOPE_LIB_DIR ${CMAKE_INSTALL_FULL_LIBDIR}/unity-scopes/clickscope/) set(SCOPE_DATA_DIR ${CMAKE_INSTALL_FULL_DATADIR}/unity/scopes/clickscope/) include(FindPkgConfig) pkg_check_modules(UNITY_SCOPES REQUIRED libunity-scopes>=0.4.0) add_definitions(${UNITY_SCOPES_CFLAGS} ${UNITY_SCOPES_CFLAGS_OTHER}) find_package (PkgConfig REQUIRED) pkg_check_modules(UBUNTUONE REQUIRED ubuntuoneauth-2.0) add_definitions(${UBUNTUONE_CFLAGS} ${UBUNTUONE_CFLAGS_OTHER}) pkg_check_modules(UBUNTU_DOWNLOAD_MANAGER_CLIENT REQUIRED ubuntu-download-manager-client) pkg_check_modules(UBUNTU_DOWNLOAD_MANAGER_COMMON REQUIRED ubuntu-download-manager-common) SET (SCOPE_LIB_VERSION 0.2.0) SET (SCOPE_LIB_SOVERSION 0) SET (SCOPE_LIB_API_VERSION 2.0) SET (SCOPE_LIB_UNVERSIONED clickscope) SET (SCOPE_LIB_NAME ${SCOPE_LIB_UNVERSIONED}-${SCOPE_LIB_API_VERSION}) add_subdirectory(autopilot) add_subdirectory(scope) add_subdirectory(data) add_subdirectory(po) # Custom targets for the tests add_custom_target (test DEPENDS test-click-scope ) add_custom_target (test-disabled DEPENDS test-click-scope-disabled ) # Add a custom target for integration tests, as they should not be run # during normal make test. add_custom_target (test-integration DEPENDS test-integration-click-scope ) # Also let "make check" and partners work. add_custom_target (check DEPENDS test ) add_custom_target (check-integration DEPENDS test-integration ) unity-scope-click-0.1+14.04.20140417/COPYING0000644000015301777760000010437412323764232020364 0ustar pbusernogroup00000000000000 GNU GENERAL PUBLIC LICENSE Version 3, 29 June 2007 Copyright (C) 2007 Free Software Foundation, Inc. Everyone is permitted to copy and distribute verbatim copies of this license document, but changing it is not allowed. Preamble The GNU General Public License is a free, copyleft license for software and other kinds of works. The licenses for most software and other practical works are designed to take away your freedom to share and change the works. By contrast, the GNU General Public License is intended to guarantee your freedom to share and change all versions of a program--to make sure it remains free software for all its users. We, the Free Software Foundation, use the GNU General Public License for most of our software; it applies also to any other work released this way by its authors. You can apply it to your programs, too. When we speak of free software, we are referring to freedom, not price. Our General Public Licenses are designed to make sure that you have the freedom to distribute copies of free software (and charge for them if you wish), that you receive source code or can get it if you want it, that you can change the software or use pieces of it in new free programs, and that you know you can do these things. To protect your rights, we need to prevent others from denying you these rights or asking you to surrender the rights. Therefore, you have certain responsibilities if you distribute copies of the software, or if you modify it: responsibilities to respect the freedom of others. For example, if you distribute copies of such a program, whether gratis or for a fee, you must pass on to the recipients the same freedoms that you received. You must make sure that they, too, receive or can get the source code. And you must show them these terms so they know their rights. Developers that use the GNU GPL protect your rights with two steps: (1) assert copyright on the software, and (2) offer you this License giving you legal permission to copy, distribute and/or modify it. For the developers' and authors' protection, the GPL clearly explains that there is no warranty for this free software. For both users' and authors' sake, the GPL requires that modified versions be marked as changed, so that their problems will not be attributed erroneously to authors of previous versions. Some devices are designed to deny users access to install or run modified versions of the software inside them, although the manufacturer can do so. This is fundamentally incompatible with the aim of protecting users' freedom to change the software. The systematic pattern of such abuse occurs in the area of products for individuals to use, which is precisely where it is most unacceptable. Therefore, we have designed this version of the GPL to prohibit the practice for those products. If such problems arise substantially in other domains, we stand ready to extend this provision to those domains in future versions of the GPL, as needed to protect the freedom of users. Finally, every program is threatened constantly by software patents. States should not allow patents to restrict development and use of software on general-purpose computers, but in those that do, we wish to avoid the special danger that patents applied to a free program could make it effectively proprietary. To prevent this, the GPL assures that patents cannot be used to render the program non-free. The precise terms and conditions for copying, distribution and modification follow. TERMS AND CONDITIONS 0. Definitions. "This License" refers to version 3 of the GNU General Public License. "Copyright" also means copyright-like laws that apply to other kinds of works, such as semiconductor masks. "The Program" refers to any copyrightable work licensed under this License. Each licensee is addressed as "you". "Licensees" and "recipients" may be individuals or organizations. To "modify" a work means to copy from or adapt all or part of the work in a fashion requiring copyright permission, other than the making of an exact copy. The resulting work is called a "modified version" of the earlier work or a work "based on" the earlier work. A "covered work" means either the unmodified Program or a work based on the Program. To "propagate" a work means to do anything with it that, without permission, would make you directly or secondarily liable for infringement under applicable copyright law, except executing it on a computer or modifying a private copy. Propagation includes copying, distribution (with or without modification), making available to the public, and in some countries other activities as well. To "convey" a work means any kind of propagation that enables other parties to make or receive copies. Mere interaction with a user through a computer network, with no transfer of a copy, is not conveying. An interactive user interface displays "Appropriate Legal Notices" to the extent that it includes a convenient and prominently visible feature that (1) displays an appropriate copyright notice, and (2) tells the user that there is no warranty for the work (except to the extent that warranties are provided), that licensees may convey the work under this License, and how to view a copy of this License. If the interface presents a list of user commands or options, such as a menu, a prominent item in the list meets this criterion. 1. Source Code. The "source code" for a work means the preferred form of the work for making modifications to it. "Object code" means any non-source form of a work. A "Standard Interface" means an interface that either is an official standard defined by a recognized standards body, or, in the case of interfaces specified for a particular programming language, one that is widely used among developers working in that language. The "System Libraries" of an executable work include anything, other than the work as a whole, that (a) is included in the normal form of packaging a Major Component, but which is not part of that Major Component, and (b) serves only to enable use of the work with that Major Component, or to implement a Standard Interface for which an implementation is available to the public in source code form. A "Major Component", in this context, means a major essential component (kernel, window system, and so on) of the specific operating system (if any) on which the executable work runs, or a compiler used to produce the work, or an object code interpreter used to run it. The "Corresponding Source" for a work in object code form means all the source code needed to generate, install, and (for an executable work) run the object code and to modify the work, including scripts to control those activities. However, it does not include the work's System Libraries, or general-purpose tools or generally available free programs which are used unmodified in performing those activities but which are not part of the work. For example, Corresponding Source includes interface definition files associated with source files for the work, and the source code for shared libraries and dynamically linked subprograms that the work is specifically designed to require, such as by intimate data communication or control flow between those subprograms and other parts of the work. The Corresponding Source need not include anything that users can regenerate automatically from other parts of the Corresponding Source. The Corresponding Source for a work in source code form is that same work. 2. Basic Permissions. All rights granted under this License are granted for the term of copyright on the Program, and are irrevocable provided the stated conditions are met. This License explicitly affirms your unlimited permission to run the unmodified Program. The output from running a covered work is covered by this License only if the output, given its content, constitutes a covered work. This License acknowledges your rights of fair use or other equivalent, as provided by copyright law. You may make, run and propagate covered works that you do not convey, without conditions so long as your license otherwise remains in force. You may convey covered works to others for the sole purpose of having them make modifications exclusively for you, or provide you with facilities for running those works, provided that you comply with the terms of this License in conveying all material for which you do not control copyright. Those thus making or running the covered works for you must do so exclusively on your behalf, under your direction and control, on terms that prohibit them from making any copies of your copyrighted material outside their relationship with you. Conveying under any other circumstances is permitted solely under the conditions stated below. Sublicensing is not allowed; section 10 makes it unnecessary. 3. Protecting Users' Legal Rights From Anti-Circumvention Law. No covered work shall be deemed part of an effective technological measure under any applicable law fulfilling obligations under article 11 of the WIPO copyright treaty adopted on 20 December 1996, or similar laws prohibiting or restricting circumvention of such measures. When you convey a covered work, you waive any legal power to forbid circumvention of technological measures to the extent such circumvention is effected by exercising rights under this License with respect to the covered work, and you disclaim any intention to limit operation or modification of the work as a means of enforcing, against the work's users, your or third parties' legal rights to forbid circumvention of technological measures. 4. Conveying Verbatim Copies. You may convey verbatim copies of the Program's source code as you receive it, in any medium, provided that you conspicuously and appropriately publish on each copy an appropriate copyright notice; keep intact all notices stating that this License and any non-permissive terms added in accord with section 7 apply to the code; keep intact all notices of the absence of any warranty; and give all recipients a copy of this License along with the Program. You may charge any price or no price for each copy that you convey, and you may offer support or warranty protection for a fee. 5. Conveying Modified Source Versions. You may convey a work based on the Program, or the modifications to produce it from the Program, in the form of source code under the terms of section 4, provided that you also meet all of these conditions: a) The work must carry prominent notices stating that you modified it, and giving a relevant date. b) The work must carry prominent notices stating that it is released under this License and any conditions added under section 7. This requirement modifies the requirement in section 4 to "keep intact all notices". c) You must license the entire work, as a whole, under this License to anyone who comes into possession of a copy. This License will therefore apply, along with any applicable section 7 additional terms, to the whole of the work, and all its parts, regardless of how they are packaged. This License gives no permission to license the work in any other way, but it does not invalidate such permission if you have separately received it. d) If the work has interactive user interfaces, each must display Appropriate Legal Notices; however, if the Program has interactive interfaces that do not display Appropriate Legal Notices, your work need not make them do so. A compilation of a covered work with other separate and independent works, which are not by their nature extensions of the covered work, and which are not combined with it such as to form a larger program, in or on a volume of a storage or distribution medium, is called an "aggregate" if the compilation and its resulting copyright are not used to limit the access or legal rights of the compilation's users beyond what the individual works permit. Inclusion of a covered work in an aggregate does not cause this License to apply to the other parts of the aggregate. 6. Conveying Non-Source Forms. You may convey a covered work in object code form under the terms of sections 4 and 5, provided that you also convey the machine-readable Corresponding Source under the terms of this License, in one of these ways: a) Convey the object code in, or embodied in, a physical product (including a physical distribution medium), accompanied by the Corresponding Source fixed on a durable physical medium customarily used for software interchange. b) Convey the object code in, or embodied in, a physical product (including a physical distribution medium), accompanied by a written offer, valid for at least three years and valid for as long as you offer spare parts or customer support for that product model, to give anyone who possesses the object code either (1) a copy of the Corresponding Source for all the software in the product that is covered by this License, on a durable physical medium customarily used for software interchange, for a price no more than your reasonable cost of physically performing this conveying of source, or (2) access to copy the Corresponding Source from a network server at no charge. c) Convey individual copies of the object code with a copy of the written offer to provide the Corresponding Source. This alternative is allowed only occasionally and noncommercially, and only if you received the object code with such an offer, in accord with subsection 6b. d) Convey the object code by offering access from a designated place (gratis or for a charge), and offer equivalent access to the Corresponding Source in the same way through the same place at no further charge. You need not require recipients to copy the Corresponding Source along with the object code. If the place to copy the object code is a network server, the Corresponding Source may be on a different server (operated by you or a third party) that supports equivalent copying facilities, provided you maintain clear directions next to the object code saying where to find the Corresponding Source. Regardless of what server hosts the Corresponding Source, you remain obligated to ensure that it is available for as long as needed to satisfy these requirements. e) Convey the object code using peer-to-peer transmission, provided you inform other peers where the object code and Corresponding Source of the work are being offered to the general public at no charge under subsection 6d. A separable portion of the object code, whose source code is excluded from the Corresponding Source as a System Library, need not be included in conveying the object code work. A "User Product" is either (1) a "consumer product", which means any tangible personal property which is normally used for personal, family, or household purposes, or (2) anything designed or sold for incorporation into a dwelling. In determining whether a product is a consumer product, doubtful cases shall be resolved in favor of coverage. For a particular product received by a particular user, "normally used" refers to a typical or common use of that class of product, regardless of the status of the particular user or of the way in which the particular user actually uses, or expects or is expected to use, the product. A product is a consumer product regardless of whether the product has substantial commercial, industrial or non-consumer uses, unless such uses represent the only significant mode of use of the product. "Installation Information" for a User Product means any methods, procedures, authorization keys, or other information required to install and execute modified versions of a covered work in that User Product from a modified version of its Corresponding Source. The information must suffice to ensure that the continued functioning of the modified object code is in no case prevented or interfered with solely because modification has been made. If you convey an object code work under this section in, or with, or specifically for use in, a User Product, and the conveying occurs as part of a transaction in which the right of possession and use of the User Product is transferred to the recipient in perpetuity or for a fixed term (regardless of how the transaction is characterized), the Corresponding Source conveyed under this section must be accompanied by the Installation Information. But this requirement does not apply if neither you nor any third party retains the ability to install modified object code on the User Product (for example, the work has been installed in ROM). The requirement to provide Installation Information does not include a requirement to continue to provide support service, warranty, or updates for a work that has been modified or installed by the recipient, or for the User Product in which it has been modified or installed. Access to a network may be denied when the modification itself materially and adversely affects the operation of the network or violates the rules and protocols for communication across the network. Corresponding Source conveyed, and Installation Information provided, in accord with this section must be in a format that is publicly documented (and with an implementation available to the public in source code form), and must require no special password or key for unpacking, reading or copying. 7. Additional Terms. "Additional permissions" are terms that supplement the terms of this License by making exceptions from one or more of its conditions. Additional permissions that are applicable to the entire Program shall be treated as though they were included in this License, to the extent that they are valid under applicable law. If additional permissions apply only to part of the Program, that part may be used separately under those permissions, but the entire Program remains governed by this License without regard to the additional permissions. When you convey a copy of a covered work, you may at your option remove any additional permissions from that copy, or from any part of it. (Additional permissions may be written to require their own removal in certain cases when you modify the work.) You may place additional permissions on material, added by you to a covered work, for which you have or can give appropriate copyright permission. Notwithstanding any other provision of this License, for material you add to a covered work, you may (if authorized by the copyright holders of that material) supplement the terms of this License with terms: a) Disclaiming warranty or limiting liability differently from the terms of sections 15 and 16 of this License; or b) Requiring preservation of specified reasonable legal notices or author attributions in that material or in the Appropriate Legal Notices displayed by works containing it; or c) Prohibiting misrepresentation of the origin of that material, or requiring that modified versions of such material be marked in reasonable ways as different from the original version; or d) Limiting the use for publicity purposes of names of licensors or authors of the material; or e) Declining to grant rights under trademark law for use of some trade names, trademarks, or service marks; or f) Requiring indemnification of licensors and authors of that material by anyone who conveys the material (or modified versions of it) with contractual assumptions of liability to the recipient, for any liability that these contractual assumptions directly impose on those licensors and authors. All other non-permissive additional terms are considered "further restrictions" within the meaning of section 10. If the Program as you received it, or any part of it, contains a notice stating that it is governed by this License along with a term that is a further restriction, you may remove that term. If a license document contains a further restriction but permits relicensing or conveying under this License, you may add to a covered work material governed by the terms of that license document, provided that the further restriction does not survive such relicensing or conveying. If you add terms to a covered work in accord with this section, you must place, in the relevant source files, a statement of the additional terms that apply to those files, or a notice indicating where to find the applicable terms. Additional terms, permissive or non-permissive, may be stated in the form of a separately written license, or stated as exceptions; the above requirements apply either way. 8. Termination. You may not propagate or modify a covered work except as expressly provided under this License. Any attempt otherwise to propagate or modify it is void, and will automatically terminate your rights under this License (including any patent licenses granted under the third paragraph of section 11). However, if you cease all violation of this License, then your license from a particular copyright holder is reinstated (a) provisionally, unless and until the copyright holder explicitly and finally terminates your license, and (b) permanently, if the copyright holder fails to notify you of the violation by some reasonable means prior to 60 days after the cessation. Moreover, your license from a particular copyright holder is reinstated permanently if the copyright holder notifies you of the violation by some reasonable means, this is the first time you have received notice of violation of this License (for any work) from that copyright holder, and you cure the violation prior to 30 days after your receipt of the notice. Termination of your rights under this section does not terminate the licenses of parties who have received copies or rights from you under this License. If your rights have been terminated and not permanently reinstated, you do not qualify to receive new licenses for the same material under section 10. 9. Acceptance Not Required for Having Copies. You are not required to accept this License in order to receive or run a copy of the Program. Ancillary propagation of a covered work occurring solely as a consequence of using peer-to-peer transmission to receive a copy likewise does not require acceptance. However, nothing other than this License grants you permission to propagate or modify any covered work. These actions infringe copyright if you do not accept this License. Therefore, by modifying or propagating a covered work, you indicate your acceptance of this License to do so. 10. Automatic Licensing of Downstream Recipients. Each time you convey a covered work, the recipient automatically receives a license from the original licensors, to run, modify and propagate that work, subject to this License. You are not responsible for enforcing compliance by third parties with this License. An "entity transaction" is a transaction transferring control of an organization, or substantially all assets of one, or subdividing an organization, or merging organizations. If propagation of a covered work results from an entity transaction, each party to that transaction who receives a copy of the work also receives whatever licenses to the work the party's predecessor in interest had or could give under the previous paragraph, plus a right to possession of the Corresponding Source of the work from the predecessor in interest, if the predecessor has it or can get it with reasonable efforts. You may not impose any further restrictions on the exercise of the rights granted or affirmed under this License. For example, you may not impose a license fee, royalty, or other charge for exercise of rights granted under this License, and you may not initiate litigation (including a cross-claim or counterclaim in a lawsuit) alleging that any patent claim is infringed by making, using, selling, offering for sale, or importing the Program or any portion of it. 11. Patents. A "contributor" is a copyright holder who authorizes use under this License of the Program or a work on which the Program is based. The work thus licensed is called the contributor's "contributor version". A contributor's "essential patent claims" are all patent claims owned or controlled by the contributor, whether already acquired or hereafter acquired, that would be infringed by some manner, permitted by this License, of making, using, or selling its contributor version, but do not include claims that would be infringed only as a consequence of further modification of the contributor version. For purposes of this definition, "control" includes the right to grant patent sublicenses in a manner consistent with the requirements of this License. Each contributor grants you a non-exclusive, worldwide, royalty-free patent license under the contributor's essential patent claims, to make, use, sell, offer for sale, import and otherwise run, modify and propagate the contents of its contributor version. In the following three paragraphs, a "patent license" is any express agreement or commitment, however denominated, not to enforce a patent (such as an express permission to practice a patent or covenant not to sue for patent infringement). To "grant" such a patent license to a party means to make such an agreement or commitment not to enforce a patent against the party. If you convey a covered work, knowingly relying on a patent license, and the Corresponding Source of the work is not available for anyone to copy, free of charge and under the terms of this License, through a publicly available network server or other readily accessible means, then you must either (1) cause the Corresponding Source to be so available, or (2) arrange to deprive yourself of the benefit of the patent license for this particular work, or (3) arrange, in a manner consistent with the requirements of this License, to extend the patent license to downstream recipients. "Knowingly relying" means you have actual knowledge that, but for the patent license, your conveying the covered work in a country, or your recipient's use of the covered work in a country, would infringe one or more identifiable patents in that country that you have reason to believe are valid. If, pursuant to or in connection with a single transaction or arrangement, you convey, or propagate by procuring conveyance of, a covered work, and grant a patent license to some of the parties receiving the covered work authorizing them to use, propagate, modify or convey a specific copy of the covered work, then the patent license you grant is automatically extended to all recipients of the covered work and works based on it. A patent license is "discriminatory" if it does not include within the scope of its coverage, prohibits the exercise of, or is conditioned on the non-exercise of one or more of the rights that are specifically granted under this License. You may not convey a covered work if you are a party to an arrangement with a third party that is in the business of distributing software, under which you make payment to the third party based on the extent of your activity of conveying the work, and under which the third party grants, to any of the parties who would receive the covered work from you, a discriminatory patent license (a) in connection with copies of the covered work conveyed by you (or copies made from those copies), or (b) primarily for and in connection with specific products or compilations that contain the covered work, unless you entered into that arrangement, or that patent license was granted, prior to 28 March 2007. Nothing in this License shall be construed as excluding or limiting any implied license or other defenses to infringement that may otherwise be available to you under applicable patent law. 12. No Surrender of Others' Freedom. If conditions are imposed on you (whether by court order, agreement or otherwise) that contradict the conditions of this License, they do not excuse you from the conditions of this License. If you cannot convey a covered work so as to satisfy simultaneously your obligations under this License and any other pertinent obligations, then as a consequence you may not convey it at all. For example, if you agree to terms that obligate you to collect a royalty for further conveying from those to whom you convey the Program, the only way you could satisfy both those terms and this License would be to refrain entirely from conveying the Program. 13. Use with the GNU Affero General Public License. Notwithstanding any other provision of this License, you have permission to link or combine any covered work with a work licensed under version 3 of the GNU Affero General Public License into a single combined work, and to convey the resulting work. The terms of this License will continue to apply to the part which is the covered work, but the special requirements of the GNU Affero General Public License, section 13, concerning interaction through a network will apply to the combination as such. 14. Revised Versions of this License. The Free Software Foundation may publish revised and/or new versions of the GNU General Public License from time to time. Such new versions will be similar in spirit to the present version, but may differ in detail to address new problems or concerns. Each version is given a distinguishing version number. If the Program specifies that a certain numbered version of the GNU General Public License "or any later version" applies to it, you have the option of following the terms and conditions either of that numbered version or of any later version published by the Free Software Foundation. If the Program does not specify a version number of the GNU General Public License, you may choose any version ever published by the Free Software Foundation. If the Program specifies that a proxy can decide which future versions of the GNU General Public License can be used, that proxy's public statement of acceptance of a version permanently authorizes you to choose that version for the Program. Later license versions may give you additional or different permissions. However, no additional obligations are imposed on any author or copyright holder as a result of your choosing to follow a later version. 15. Disclaimer of Warranty. THERE IS NO WARRANTY FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU. SHOULD THE PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING, REPAIR OR CORRECTION. 16. Limitation of Liability. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MODIFIES AND/OR CONVEYS THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGES. 17. Interpretation of Sections 15 and 16. If the disclaimer of warranty and limitation of liability provided above cannot be given local legal effect according to their terms, reviewing courts shall apply local law that most closely approximates an absolute waiver of all civil liability in connection with the Program, unless a warranty or assumption of liability accompanies a copy of the Program in return for a fee. END OF TERMS AND CONDITIONS How to Apply These Terms to Your New Programs If you develop a new program, and you want it to be of the greatest possible use to the public, the best way to achieve this is to make it free software which everyone can redistribute and change under these terms. To do so, attach the following notices to the program. It is safest to attach them to the start of each source file to most effectively state the exclusion of warranty; and each file should have at least the "copyright" line and a pointer to where the full notice is found. Copyright (C) This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program. If not, see . Also add information on how to contact you by electronic and paper mail. If the program does terminal interaction, make it output a short notice like this when it starts in an interactive mode: Copyright (C) This program comes with ABSOLUTELY NO WARRANTY; for details type `show w'. This is free software, and you are welcome to redistribute it under certain conditions; type `show c' for details. The hypothetical commands `show w' and `show c' should show the appropriate parts of the General Public License. Of course, your program's commands might be different; for a GUI interface, you would use an "about box". You should also get your employer (if you work as a programmer) or school, if any, to sign a "copyright disclaimer" for the program, if necessary. For more information on this, and how to apply and follow the GNU GPL, see . The GNU General Public License does not permit incorporating your program into proprietary programs. If your program is a subroutine library, you may consider it more useful to permit linking proprietary applications with the library. If this is what you want to do, use the GNU Lesser General Public License instead of this License. But first, please read . unity-scope-click-0.1+14.04.20140417/AUTHORS0000644000015301777760000000014312323764232020366 0ustar pbusernogroup00000000000000Alejandro J. Cura Manuel de la Pena unity-scope-click-0.1+14.04.20140417/README0000644000015301777760000000000012323764232020166 0ustar pbusernogroup00000000000000unity-scope-click-0.1+14.04.20140417/MAINTAINERS0000644000015301777760000000006112323764232021012 0ustar pbusernogroup00000000000000Alejandro J. Cura