Merge lp:~nataliabidart/software-center/fix-977931 into lp:software-center/5.2
- fix-977931
- Merge into 5.2
Proposed by
Natalia Bidart
Status: | Merged |
---|---|
Merged at revision: | 3027 |
Proposed branch: | lp:~nataliabidart/software-center/fix-977931 |
Merge into: | lp:software-center/5.2 |
Diff against target: |
1180 lines (+627/-146) 19 files modified
README (+1/-0) softwarecenter/backend/spawn_helper.py (+3/-0) softwarecenter/db/__init__.py (+7/-2) softwarecenter/db/database.py (+4/-0) softwarecenter/db/debfile.py (+38/-21) softwarecenter/enums.py (+6/-0) softwarecenter/testutils.py (+11/-1) softwarecenter/ui/gtk3/SimpleGtkbuilderApp.py (+2/-1) softwarecenter/ui/gtk3/app.py (+151/-89) softwarecenter/ui/gtk3/models/appstore2.py (+1/-0) softwarecenter/ui/gtk3/session/appmanager.py (+5/-0) softwarecenter/ui/gtk3/session/viewmanager.py (+5/-0) softwarecenter/ui/gtk3/views/appdetailsview.py (+2/-2) softwarecenter/ui/gtk3/widgets/videoplayer.py (+5/-1) test/gtk3/test_app.py (+357/-0) test/gtk3/test_debfile_view.py (+3/-8) test/gtk3/test_purchase.py (+8/-12) test/test_database.py (+8/-3) test/test_debfileapplication.py (+10/-6) |
To merge this branch: | bzr merge lp:~nataliabidart/software-center/fix-977931 |
Related bugs: |
Reviewer | Review Type | Date Requested | Status |
---|---|---|---|
Michael Vogt | Approve | ||
Review via email: mp+106011@code.launchpad.net |
This proposal supersedes a proposal from 2012-04-17.
Commit message
- Unified package string parsing into a single method that will be used from either the command line arguments, or from the dbus method 'bringToFront'. This way, search will be consistent between all entry points.
- Also added proper test suites for the above.
Description of the change
To post a comment you must log in.
Revision history for this message
Michael Vogt (mvo) wrote : Posted in a previous version of this proposal | # |
Revision history for this message
Natalia Bidart (nataliabidart) wrote : Posted in a previous version of this proposal | # |
Merged the branch suggested by mvo adding a minor tweaks about detetcting .deb files.
- 2993. By Natalia Bidart
-
Merged 5.2 in.
- 2994. By Natalia Bidart
-
Merged 5.2 in.
Revision history for this message
Michael Vogt (mvo) wrote : | # |
This looks great, I will merge it now into 5.2 (and it will be merged into trunk via 5.2 too).
review:
Approve
Preview Diff
[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
1 | === modified file 'README' | |||
2 | --- README 2012-03-08 18:08:49 +0000 | |||
3 | +++ README 2012-05-21 15:37:20 +0000 | |||
4 | @@ -53,6 +53,7 @@ | |||
5 | 53 | SOFTWARE_CENTER_DISTRO_CODENAME - overwrite "lsb_release -c -s" output | 53 | SOFTWARE_CENTER_DISTRO_CODENAME - overwrite "lsb_release -c -s" output |
6 | 54 | SOFTWARE_CENTER_ARCHITECTURE - overwrite the current architecture | 54 | SOFTWARE_CENTER_ARCHITECTURE - overwrite the current architecture |
7 | 55 | SOFTWARE_CENTER_NO_SC_AGENT - disable the software-center-agent | 55 | SOFTWARE_CENTER_NO_SC_AGENT - disable the software-center-agent |
8 | 56 | SOFTWARE_CENTER_DISABLE_SPAWN_HELPER - disable everything that is run via the "SpawnHelper", i.e. recommender-agent, software-center-agent, reviews | ||
9 | 56 | SOFTWARE_CENTER_DEBUG_TABS - show notebook tabs for debugging | 57 | SOFTWARE_CENTER_DEBUG_TABS - show notebook tabs for debugging |
10 | 57 | SOFTWARE_CENTER_FORCE_DISABLE_CERTS_CHECK - disables certificates checking in webkit views (for use in test environments) | 58 | SOFTWARE_CENTER_FORCE_DISABLE_CERTS_CHECK - disables certificates checking in webkit views (for use in test environments) |
11 | 58 | SOFTWARE_CENTER_FORCE_NON_SSL - disable SSL (for use in test environments) | 59 | SOFTWARE_CENTER_FORCE_NON_SSL - disable SSL (for use in test environments) |
12 | 59 | 60 | ||
13 | === modified file 'softwarecenter/backend/spawn_helper.py' | |||
14 | --- softwarecenter/backend/spawn_helper.py 2012-03-19 13:35:47 +0000 | |||
15 | +++ softwarecenter/backend/spawn_helper.py 2012-05-21 15:37:20 +0000 | |||
16 | @@ -86,6 +86,9 @@ | |||
17 | 86 | self.run(cmd) | 86 | self.run(cmd) |
18 | 87 | 87 | ||
19 | 88 | def run(self, cmd): | 88 | def run(self, cmd): |
20 | 89 | # only useful for debugging | ||
21 | 90 | if "SOFTWARE_CENTER_DISABLE_SPAWN_HELPER" in os.environ: | ||
22 | 91 | return | ||
23 | 89 | self._cmd = cmd | 92 | self._cmd = cmd |
24 | 90 | (pid, stdin, stdout, stderr) = GObject.spawn_async( | 93 | (pid, stdin, stdout, stderr) = GObject.spawn_async( |
25 | 91 | cmd, flags=GObject.SPAWN_DO_NOT_REAP_CHILD, | 94 | cmd, flags=GObject.SPAWN_DO_NOT_REAP_CHILD, |
26 | 92 | 95 | ||
27 | === modified file 'softwarecenter/db/__init__.py' | |||
28 | --- softwarecenter/db/__init__.py 2012-03-15 04:30:04 +0000 | |||
29 | +++ softwarecenter/db/__init__.py 2012-05-21 15:37:20 +0000 | |||
30 | @@ -1,9 +1,14 @@ | |||
31 | 1 | import logging | 1 | import logging |
32 | 2 | |||
33 | 2 | try: | 3 | try: |
35 | 3 | from debfile import DebFileApplication | 4 | from debfile import DebFileApplication, DebFileOpenError |
36 | 4 | DebFileApplication # pyflakes | 5 | DebFileApplication # pyflakes |
37 | 6 | DebFileOpenError # pyflakes | ||
38 | 5 | except: | 7 | except: |
39 | 6 | logging.exception("DebFileApplication import") | 8 | logging.exception("DebFileApplication import") |
40 | 7 | 9 | ||
42 | 8 | class DebFileApplication(): | 10 | class DebFileApplication(object): |
43 | 11 | pass | ||
44 | 12 | |||
45 | 13 | class DebFileOpenError(Exception): | ||
46 | 9 | pass | 14 | pass |
47 | 10 | 15 | ||
48 | === modified file 'softwarecenter/db/database.py' | |||
49 | --- softwarecenter/db/database.py 2012-04-12 10:15:36 +0000 | |||
50 | +++ softwarecenter/db/database.py 2012-05-21 15:37:20 +0000 | |||
51 | @@ -139,6 +139,10 @@ | |||
52 | 139 | 139 | ||
53 | 140 | def __init__(self, pathname=None, cache=None): | 140 | def __init__(self, pathname=None, cache=None): |
54 | 141 | GObject.GObject.__init__(self) | 141 | GObject.GObject.__init__(self) |
55 | 142 | # initialize at creation time to avoid spurious AttributeError | ||
56 | 143 | self._use_agent = False | ||
57 | 144 | self._use_axi = False | ||
58 | 145 | |||
59 | 142 | if pathname is None: | 146 | if pathname is None: |
60 | 143 | pathname = softwarecenter.paths.XAPIAN_PATH | 147 | pathname = softwarecenter.paths.XAPIAN_PATH |
61 | 144 | self._db_pathname = pathname | 148 | self._db_pathname = pathname |
62 | 145 | 149 | ||
63 | === modified file 'softwarecenter/db/debfile.py' | |||
64 | --- softwarecenter/db/debfile.py 2012-03-29 00:18:23 +0000 | |||
65 | +++ softwarecenter/db/debfile.py 2012-05-21 15:37:20 +0000 | |||
66 | @@ -27,12 +27,28 @@ | |||
67 | 27 | from softwarecenter.utils import ExecutionTime, utf8 | 27 | from softwarecenter.utils import ExecutionTime, utf8 |
68 | 28 | 28 | ||
69 | 29 | 29 | ||
70 | 30 | DEB_MIME_TYPE = 'application/x-debian-package' | ||
71 | 31 | |||
72 | 32 | |||
73 | 33 | def is_deb_file(debfile): | ||
74 | 34 | mtype = guess_type(debfile) | ||
75 | 35 | return mtype is not None and DEB_MIME_TYPE in mtype | ||
76 | 36 | |||
77 | 37 | |||
78 | 38 | class DebFileOpenError(Exception): | ||
79 | 39 | """ Raised if a DebFile fails to open """ | ||
80 | 40 | |||
81 | 41 | def __init__(self, msg, path): | ||
82 | 42 | super(DebFileOpenError, self).__init__(msg) | ||
83 | 43 | self.path = path | ||
84 | 44 | |||
85 | 45 | |||
86 | 30 | class DebFileApplication(Application): | 46 | class DebFileApplication(Application): |
87 | 31 | 47 | ||
88 | 32 | def __init__(self, debfile): | 48 | def __init__(self, debfile): |
92 | 33 | # sanity check | 49 | if not is_deb_file(debfile): |
93 | 34 | if not debfile.endswith(".deb"): | 50 | raise DebFileOpenError("Could not open %r." % debfile, debfile) |
94 | 35 | raise ValueError("Need a deb file, got '%s'" % debfile) | 51 | |
95 | 36 | # work out debname/appname | 52 | # work out debname/appname |
96 | 37 | debname = os.path.splitext(os.path.basename(debfile))[0] | 53 | debname = os.path.splitext(os.path.basename(debfile))[0] |
97 | 38 | pkgname = debname.split('_')[0].lower() | 54 | pkgname = debname.split('_')[0].lower() |
98 | @@ -50,7 +66,21 @@ | |||
99 | 50 | def __init__(self, db, doc=None, application=None): | 66 | def __init__(self, db, doc=None, application=None): |
100 | 51 | super(AppDetailsDebFile, self).__init__(db, doc, application) | 67 | super(AppDetailsDebFile, self).__init__(db, doc, application) |
101 | 52 | if doc: | 68 | if doc: |
103 | 53 | raise ValueError("doc must be None for deb files") | 69 | raise DebFileOpenError("AppDetailsDebFile: doc must be None.") |
104 | 70 | |||
105 | 71 | self._error = None | ||
106 | 72 | # check errors before creating the DebPackage | ||
107 | 73 | if not os.path.exists(self._app.request): | ||
108 | 74 | self._error = _("Not found") | ||
109 | 75 | self._error_not_found = utf8(_(u"The file \u201c%s\u201d " | ||
110 | 76 | "does not exist.")) % utf8(self._app.request) | ||
111 | 77 | elif not is_deb_file(self._app.request): | ||
112 | 78 | self._error = _("Not found") | ||
113 | 79 | self._error_not_found = utf8(_(u"The file \u201c%s\u201d " | ||
114 | 80 | "is not a software package.")) % utf8(self._app.request) | ||
115 | 81 | |||
116 | 82 | if self._error is not None: | ||
117 | 83 | return | ||
118 | 54 | 84 | ||
119 | 55 | try: | 85 | try: |
120 | 56 | with ExecutionTime("create DebPackage"): | 86 | with ExecutionTime("create DebPackage"): |
121 | @@ -59,23 +89,10 @@ | |||
122 | 59 | self._deb = DebPackage(self._app.request, self._cache._cache) | 89 | self._deb = DebPackage(self._app.request, self._cache._cache) |
123 | 60 | except: | 90 | except: |
124 | 61 | self._deb = None | 91 | self._deb = None |
142 | 62 | self._pkg = None | 92 | # deb files which are corrupt |
143 | 63 | if not os.path.exists(self._app.request): | 93 | self._error = _("Internal Error") |
144 | 64 | self._error = _("Not found") | 94 | self._error_not_found = utf8(_(u"The file \u201c%s\u201d " |
145 | 65 | self._error_not_found = utf8(_(u"The file \u201c%s\u201d " | 95 | "could not be opened.")) % utf8(self._app.request) |
129 | 66 | "does not exist.")) % utf8(self._app.request) | ||
130 | 67 | else: | ||
131 | 68 | mimetype = guess_type(self._app.request) | ||
132 | 69 | if mimetype[0] != "application/x-debian-package": | ||
133 | 70 | self._error = _("Not found") | ||
134 | 71 | self._error_not_found = utf8(_(u"The file \u201c%s\u201d " | ||
135 | 72 | "is not a software package.")) % utf8( | ||
136 | 73 | self._app.request) | ||
137 | 74 | else: | ||
138 | 75 | # deb files which are corrupt | ||
139 | 76 | self._error = _("Internal Error") | ||
140 | 77 | self._error_not_found = utf8(_(u"The file \u201c%s\u201d " | ||
141 | 78 | "could not be opened.")) % utf8(self._app.request) | ||
146 | 79 | return | 96 | return |
147 | 80 | 97 | ||
148 | 81 | if self.pkgname and self.pkgname != self._app.pkgname: | 98 | if self.pkgname and self.pkgname != self._app.pkgname: |
149 | 82 | 99 | ||
150 | === modified file 'softwarecenter/enums.py' | |||
151 | --- softwarecenter/enums.py 2012-03-21 20:54:46 +0000 | |||
152 | +++ softwarecenter/enums.py 2012-05-21 15:37:20 +0000 | |||
153 | @@ -237,6 +237,12 @@ | |||
154 | 237 | REPAIR = "repair_dependencies" | 237 | REPAIR = "repair_dependencies" |
155 | 238 | 238 | ||
156 | 239 | 239 | ||
157 | 240 | # Search separators | ||
158 | 241 | class SearchSeparators: | ||
159 | 242 | REGULAR = " " | ||
160 | 243 | PACKAGE = "," | ||
161 | 244 | |||
162 | 245 | |||
163 | 240 | # mouse event codes for back/forward buttons | 246 | # mouse event codes for back/forward buttons |
164 | 241 | # TODO: consider whether we ought to get these values from gconf so that we | 247 | # TODO: consider whether we ought to get these values from gconf so that we |
165 | 242 | # can be sure to use the corresponding values used by Nautilus: | 248 | # can be sure to use the corresponding values used by Nautilus: |
166 | 243 | 249 | ||
167 | === modified file 'softwarecenter/testutils.py' | |||
168 | --- softwarecenter/testutils.py 2012-03-19 14:23:52 +0000 | |||
169 | +++ softwarecenter/testutils.py 2012-05-21 15:37:20 +0000 | |||
170 | @@ -22,6 +22,7 @@ | |||
171 | 22 | import tempfile | 22 | import tempfile |
172 | 23 | import time | 23 | import time |
173 | 24 | 24 | ||
174 | 25 | from mock import Mock | ||
175 | 25 | 26 | ||
176 | 26 | m_dbus = m_polkit = m_aptd = None | 27 | m_dbus = m_polkit = m_aptd = None |
177 | 27 | 28 | ||
178 | @@ -145,7 +146,6 @@ | |||
179 | 145 | """ take a application and return a app where the details are a mock | 146 | """ take a application and return a app where the details are a mock |
180 | 146 | of the real details so they can easily be modified | 147 | of the real details so they can easily be modified |
181 | 147 | """ | 148 | """ |
182 | 148 | from mock import Mock | ||
183 | 149 | import copy | 149 | import copy |
184 | 150 | app = copy.copy(real_app) | 150 | app = copy.copy(real_app) |
185 | 151 | db = get_test_db() | 151 | db = get_test_db() |
186 | @@ -160,6 +160,16 @@ | |||
187 | 160 | return app | 160 | return app |
188 | 161 | 161 | ||
189 | 162 | 162 | ||
190 | 163 | def get_mock_options(): | ||
191 | 164 | """Return a mock suitable to act as SoftwareCenterAppGtk3's options.""" | ||
192 | 165 | mock_options = Mock() | ||
193 | 166 | mock_options.display_navlog = False | ||
194 | 167 | mock_options.disable_apt_xapian_index = False | ||
195 | 168 | mock_options.disable_buy = False | ||
196 | 169 | |||
197 | 170 | return mock_options | ||
198 | 171 | |||
199 | 172 | |||
200 | 163 | def setup_test_env(): | 173 | def setup_test_env(): |
201 | 164 | """ Setup environment suitable for running the test/* code in a checkout. | 174 | """ Setup environment suitable for running the test/* code in a checkout. |
202 | 165 | This includes PYTHONPATH, sys.path and softwarecenter.paths.datadir. | 175 | This includes PYTHONPATH, sys.path and softwarecenter.paths.datadir. |
203 | 166 | 176 | ||
204 | === modified file 'softwarecenter/ui/gtk3/SimpleGtkbuilderApp.py' | |||
205 | --- softwarecenter/ui/gtk3/SimpleGtkbuilderApp.py 2012-03-15 09:32:18 +0000 | |||
206 | +++ softwarecenter/ui/gtk3/SimpleGtkbuilderApp.py 2012-05-21 15:37:20 +0000 | |||
207 | @@ -23,9 +23,10 @@ | |||
208 | 23 | 23 | ||
209 | 24 | 24 | ||
210 | 25 | # based on SimpleGladeApp | 25 | # based on SimpleGladeApp |
212 | 26 | class SimpleGtkbuilderApp: | 26 | class SimpleGtkbuilderApp(object): |
213 | 27 | 27 | ||
214 | 28 | def __init__(self, path, domain): | 28 | def __init__(self, path, domain): |
215 | 29 | super(SimpleGtkbuilderApp, self).__init__() | ||
216 | 29 | self.builder = Gtk.Builder() | 30 | self.builder = Gtk.Builder() |
217 | 30 | self.builder.set_translation_domain(domain) | 31 | self.builder.set_translation_domain(domain) |
218 | 31 | self.builder.add_from_file(path) | 32 | self.builder.add_from_file(path) |
219 | 32 | 33 | ||
220 | === modified file 'softwarecenter/ui/gtk3/app.py' | |||
221 | --- softwarecenter/ui/gtk3/app.py 2012-05-15 18:37:27 +0000 | |||
222 | +++ softwarecenter/ui/gtk3/app.py 2012-05-21 15:37:20 +0000 | |||
223 | @@ -31,6 +31,7 @@ | |||
224 | 31 | import gettext | 31 | import gettext |
225 | 32 | import logging | 32 | import logging |
226 | 33 | import os | 33 | import os |
227 | 34 | import re | ||
228 | 34 | import subprocess | 35 | import subprocess |
229 | 35 | import sys | 36 | import sys |
230 | 36 | import xapian | 37 | import xapian |
231 | @@ -47,28 +48,35 @@ | |||
232 | 47 | 48 | ||
233 | 48 | # db imports | 49 | # db imports |
234 | 49 | from softwarecenter.db.application import Application | 50 | from softwarecenter.db.application import Application |
236 | 50 | from softwarecenter.db import DebFileApplication | 51 | from softwarecenter.db import DebFileApplication, DebFileOpenError |
237 | 51 | from softwarecenter.i18n import init_locale | 52 | from softwarecenter.i18n import init_locale |
238 | 52 | 53 | ||
239 | 53 | # misc imports | 54 | # misc imports |
240 | 54 | from softwarecenter.plugin import PluginManager | 55 | from softwarecenter.plugin import PluginManager |
241 | 55 | from softwarecenter.paths import SOFTWARE_CENTER_PLUGIN_DIRS | 56 | from softwarecenter.paths import SOFTWARE_CENTER_PLUGIN_DIRS |
258 | 56 | from softwarecenter.enums import (Icons, | 57 | from softwarecenter.enums import ( |
259 | 57 | PkgStates, | 58 | AppActions, |
260 | 58 | ViewPages, | 59 | DB_SCHEMA_VERSION, |
261 | 59 | AppActions, | 60 | Icons, |
262 | 60 | DB_SCHEMA_VERSION, | 61 | MOUSE_EVENT_FORWARD_BUTTON, |
263 | 61 | MOUSE_EVENT_FORWARD_BUTTON, | 62 | MOUSE_EVENT_BACK_BUTTON, |
264 | 62 | MOUSE_EVENT_BACK_BUTTON, | 63 | PkgStates, |
265 | 63 | SOFTWARE_CENTER_TOS_LINK, | 64 | SearchSeparators, |
266 | 64 | SOFTWARE_CENTER_NAME_KEYRING) | 65 | SOFTWARE_CENTER_TOS_LINK, |
267 | 65 | from softwarecenter.utils import (clear_token_from_ubuntu_sso_sync, | 66 | SOFTWARE_CENTER_NAME_KEYRING, |
268 | 66 | get_http_proxy_string_from_gsettings, | 67 | ViewPages, |
269 | 67 | wait_for_apt_cache_ready, | 68 | ) |
270 | 68 | ExecutionTime, | 69 | from softwarecenter.utils import ( |
271 | 69 | is_unity_running) | 70 | clear_token_from_ubuntu_sso_sync, |
272 | 70 | from softwarecenter.ui.gtk3.utils import (get_sc_icon_theme, | 71 | get_http_proxy_string_from_gsettings, |
273 | 71 | init_sc_css_provider) | 72 | wait_for_apt_cache_ready, |
274 | 73 | ExecutionTime, | ||
275 | 74 | is_unity_running, | ||
276 | 75 | ) | ||
277 | 76 | from softwarecenter.ui.gtk3.utils import ( | ||
278 | 77 | get_sc_icon_theme, | ||
279 | 78 | init_sc_css_provider, | ||
280 | 79 | ) | ||
281 | 72 | from softwarecenter.version import VERSION | 80 | from softwarecenter.version import VERSION |
282 | 73 | from softwarecenter.db.database import StoreDatabase | 81 | from softwarecenter.db.database import StoreDatabase |
283 | 74 | try: | 82 | try: |
284 | @@ -87,10 +95,14 @@ | |||
285 | 87 | from softwarecenter.ui.gtk3.panes.historypane import HistoryPane | 95 | from softwarecenter.ui.gtk3.panes.historypane import HistoryPane |
286 | 88 | from softwarecenter.ui.gtk3.panes.globalpane import GlobalPane | 96 | from softwarecenter.ui.gtk3.panes.globalpane import GlobalPane |
287 | 89 | from softwarecenter.ui.gtk3.panes.pendingpane import PendingPane | 97 | from softwarecenter.ui.gtk3.panes.pendingpane import PendingPane |
290 | 90 | from softwarecenter.ui.gtk3.session.appmanager import (ApplicationManager, | 98 | from softwarecenter.ui.gtk3.session.appmanager import ( |
291 | 91 | get_appmanager) | 99 | ApplicationManager, |
292 | 100 | get_appmanager, | ||
293 | 101 | ) | ||
294 | 92 | from softwarecenter.ui.gtk3.session.viewmanager import ( | 102 | from softwarecenter.ui.gtk3.session.viewmanager import ( |
296 | 93 | ViewManager, get_viewmanager) | 103 | ViewManager, |
297 | 104 | get_viewmanager, | ||
298 | 105 | ) | ||
299 | 94 | from softwarecenter.ui.gtk3.widgets.recommendations import ( | 106 | from softwarecenter.ui.gtk3.widgets.recommendations import ( |
300 | 95 | RecommendationsOptInDialog) | 107 | RecommendationsOptInDialog) |
301 | 96 | 108 | ||
302 | @@ -112,6 +124,10 @@ | |||
303 | 112 | from gi.repository import Gdk | 124 | from gi.repository import Gdk |
304 | 113 | 125 | ||
305 | 114 | LOG = logging.getLogger(__name__) | 126 | LOG = logging.getLogger(__name__) |
306 | 127 | PACKAGE_PREFIX = 'apt:' | ||
307 | 128 | # "apt:///" is a valid prefix for 'apt:pkgname' in alt+F2 in gnome | ||
308 | 129 | PACKAGE_PREFIX_REGEX = re.compile('^%s(?:/{2,3})*' % PACKAGE_PREFIX) | ||
309 | 130 | SEARCH_PREFIX = 'search:' | ||
310 | 115 | 131 | ||
311 | 116 | 132 | ||
312 | 117 | # py3 compat | 133 | # py3 compat |
313 | @@ -119,6 +135,64 @@ | |||
314 | 119 | return isinstance(func, collections.Callable) | 135 | return isinstance(func, collections.Callable) |
315 | 120 | 136 | ||
316 | 121 | 137 | ||
317 | 138 | def parse_packages_args(packages): | ||
318 | 139 | search_text = '' | ||
319 | 140 | app = None | ||
320 | 141 | |||
321 | 142 | # avoid treating strings as sequences ('foo' should not be 'f', 'o', 'o') | ||
322 | 143 | if isinstance(packages, basestring): | ||
323 | 144 | packages = (packages,) | ||
324 | 145 | |||
325 | 146 | if not isinstance(packages, collections.Iterable): | ||
326 | 147 | LOG.warning('show_available_packages: argument is not an iterable %r', | ||
327 | 148 | packages) | ||
328 | 149 | return search_text, app | ||
329 | 150 | |||
330 | 151 | items = [] # make a copy of the given sequence | ||
331 | 152 | for arg in packages: | ||
332 | 153 | # support both "pkg1 pkg" and "pkg1,pkg2" (and "pkg1,pkg2 pkg3") | ||
333 | 154 | if "," in arg: | ||
334 | 155 | items.extend(arg.split(SearchSeparators.PACKAGE)) | ||
335 | 156 | else: | ||
336 | 157 | items.append(arg) | ||
337 | 158 | |||
338 | 159 | if len(items) > 0: | ||
339 | 160 | # allow s-c to be called with a search term | ||
340 | 161 | if items[0].startswith(SEARCH_PREFIX): | ||
341 | 162 | # remove the initial search prefix | ||
342 | 163 | items[0] = items[0].replace(SEARCH_PREFIX, '', 1) | ||
343 | 164 | search_text = SearchSeparators.REGULAR.join(items) | ||
344 | 165 | else: | ||
345 | 166 | # strip away the initial apt: prefix, if present | ||
346 | 167 | items[0] = re.sub(PACKAGE_PREFIX_REGEX, '', items[0]) | ||
347 | 168 | if len(items) > 1: | ||
348 | 169 | # turn multiple packages into a search with "," as separator | ||
349 | 170 | search_text = SearchSeparators.PACKAGE.join(items) | ||
350 | 171 | |||
351 | 172 | if not search_text and len(items) == 1: | ||
352 | 173 | request = items[0] | ||
353 | 174 | # are we dealing with a path? | ||
354 | 175 | if os.path.exists(request) and not os.path.isdir(request): | ||
355 | 176 | if not request.startswith('/'): | ||
356 | 177 | # we may have been given a relative path | ||
357 | 178 | request = os.path.abspath(request) | ||
358 | 179 | # will raise DebOpenFileError if request is invalid | ||
359 | 180 | app = DebFileApplication(request) | ||
360 | 181 | else: | ||
361 | 182 | # package from archive | ||
362 | 183 | # if there is a "/" in the string consider it as tuple | ||
363 | 184 | # of (pkgname, appname) for exact matching (used by | ||
364 | 185 | # e.g. unity | ||
365 | 186 | (pkgname, sep, appname) = request.partition("/") | ||
366 | 187 | if pkgname or appname: | ||
367 | 188 | app = Application(appname, pkgname) | ||
368 | 189 | else: | ||
369 | 190 | LOG.warning('show_available_packages: received %r but ' | ||
370 | 191 | 'can not build an Application from it.', request) | ||
371 | 192 | |||
372 | 193 | return search_text, app | ||
373 | 194 | |||
374 | 195 | |||
375 | 122 | class SoftwarecenterDbusController(dbus.service.Object): | 196 | class SoftwarecenterDbusController(dbus.service.Object): |
376 | 123 | """ | 197 | """ |
377 | 124 | This is a helper to provide the SoftwarecenterIFace | 198 | This is a helper to provide the SoftwarecenterIFace |
378 | @@ -158,13 +232,16 @@ | |||
379 | 158 | # the size of the icon for dialogs | 232 | # the size of the icon for dialogs |
380 | 159 | APP_ICON_SIZE = Gtk.IconSize.DIALOG | 233 | APP_ICON_SIZE = Gtk.IconSize.DIALOG |
381 | 160 | 234 | ||
382 | 235 | START_DBUS = True | ||
383 | 236 | |||
384 | 161 | def __init__(self, datadir, xapian_base_path, options, args=None): | 237 | def __init__(self, datadir, xapian_base_path, options, args=None): |
388 | 162 | # setup dbus and exit if there is another instance already | 238 | self.dbusControler = None |
389 | 163 | # running | 239 | if self.START_DBUS: |
390 | 164 | self.setup_dbus_or_bring_other_instance_to_front(args) | 240 | # setup dbus and exit if there is another instance already running |
391 | 241 | self.setup_dbus_or_bring_other_instance_to_front(args) | ||
392 | 165 | 242 | ||
393 | 166 | self.datadir = datadir | 243 | self.datadir = datadir |
395 | 167 | SimpleGtkbuilderApp.__init__(self, | 244 | super(SoftwareCenterAppGtk3, self).__init__( |
396 | 168 | datadir + "/ui/gtk3/SoftwareCenter.ui", | 245 | datadir + "/ui/gtk3/SoftwareCenter.ui", |
397 | 169 | "software-center") | 246 | "software-center") |
398 | 170 | gettext.bindtextdomain("software-center", "/usr/share/locale") | 247 | gettext.bindtextdomain("software-center", "/usr/share/locale") |
399 | @@ -525,6 +602,18 @@ | |||
400 | 525 | def on_review_stats_loaded(self, reviews): | 602 | def on_review_stats_loaded(self, reviews): |
401 | 526 | LOG.debug("on_review_stats_loaded: '%s'" % len(reviews)) | 603 | LOG.debug("on_review_stats_loaded: '%s'" % len(reviews)) |
402 | 527 | 604 | ||
403 | 605 | def destroy(self): | ||
404 | 606 | """Destroy this instance and every used resource.""" | ||
405 | 607 | self.window_main.destroy() | ||
406 | 608 | |||
407 | 609 | # remove global instances of Managers | ||
408 | 610 | self.app_manager.destroy() | ||
409 | 611 | self.view_manager.destroy() | ||
410 | 612 | |||
411 | 613 | if self.dbusControler is not None: | ||
412 | 614 | # ensure that the dbus controller is really gone | ||
413 | 615 | self.dbusControler.stop() | ||
414 | 616 | |||
415 | 528 | def close_app(self): | 617 | def close_app(self): |
416 | 529 | """ perform tasks like save-state etc when the application is | 618 | """ perform tasks like save-state etc when the application is |
417 | 530 | exited | 619 | exited |
418 | @@ -539,15 +628,14 @@ | |||
419 | 539 | if hasattr(self, "glaunchpad"): | 628 | if hasattr(self, "glaunchpad"): |
420 | 540 | self.glaunchpad.shutdown() | 629 | self.glaunchpad.shutdown() |
421 | 541 | self.save_state() | 630 | self.save_state() |
422 | 631 | self.destroy() | ||
423 | 632 | |||
424 | 542 | # this will not throw exceptions in pygi but "only" log via g_critical | 633 | # this will not throw exceptions in pygi but "only" log via g_critical |
425 | 543 | # to the terminal but it might in the future so we add a handler here | 634 | # to the terminal but it might in the future so we add a handler here |
426 | 544 | try: | 635 | try: |
427 | 545 | Gtk.main_quit() | 636 | Gtk.main_quit() |
428 | 546 | except: | 637 | except: |
429 | 547 | LOG.exception("Gtk.main_quit failed") | 638 | LOG.exception("Gtk.main_quit failed") |
430 | 548 | # ensure that the dbus controller is really gone, just for good | ||
431 | 549 | # measure | ||
432 | 550 | self.dbusControler.stop() | ||
433 | 551 | # exit here explictely to ensure that no further gtk event loops or | 639 | # exit here explictely to ensure that no further gtk event loops or |
434 | 552 | # threads run and cause havoc on exit (LP: #914393) | 640 | # threads run and cause havoc on exit (LP: #914393) |
435 | 553 | sys.exit(0) | 641 | sys.exit(0) |
436 | @@ -1217,75 +1305,49 @@ | |||
437 | 1217 | bus_name = dbus.service.BusName('com.ubuntu.Softwarecenter', bus) | 1305 | bus_name = dbus.service.BusName('com.ubuntu.Softwarecenter', bus) |
438 | 1218 | self.dbusControler = SoftwarecenterDbusController(self, bus_name) | 1306 | self.dbusControler = SoftwarecenterDbusController(self, bus_name) |
439 | 1219 | 1307 | ||
440 | 1308 | @wait_for_apt_cache_ready | ||
441 | 1309 | def show_app(self, app): | ||
442 | 1310 | """Show 'app' in the installed pane if is installed. | ||
443 | 1311 | |||
444 | 1312 | If 'app' is not installed, show it in the available pane. | ||
445 | 1313 | |||
446 | 1314 | """ | ||
447 | 1315 | if (app.pkgname in self.cache and self.cache[app.pkgname].installed): | ||
448 | 1316 | with ExecutionTime("installed_pane.init_view()"): | ||
449 | 1317 | self.installed_pane.init_view() | ||
450 | 1318 | with ExecutionTime("installed_pane.show_app()"): | ||
451 | 1319 | self.installed_pane.show_app(app) | ||
452 | 1320 | else: | ||
453 | 1321 | self.available_pane.init_view() | ||
454 | 1322 | self.available_pane.show_app(app) | ||
455 | 1323 | |||
456 | 1220 | def show_available_packages(self, packages): | 1324 | def show_available_packages(self, packages): |
457 | 1221 | """ Show packages given as arguments in the available_pane | 1325 | """ Show packages given as arguments in the available_pane |
458 | 1222 | If the list of packages is only one element long show that, | 1326 | If the list of packages is only one element long show that, |
459 | 1223 | otherwise turn it into a comma seperated search | 1327 | otherwise turn it into a comma seperated search |
460 | 1224 | """ | 1328 | """ |
491 | 1225 | # strip away the apt: prefix | 1329 | try: |
492 | 1226 | if packages and packages[0].startswith("apt:///"): | 1330 | search_text, app = parse_packages_args(packages) |
493 | 1227 | # this is for 'apt:pkgname' in alt+F2 in gnome | 1331 | except DebFileOpenError as e: |
494 | 1228 | packages[0] = packages[0].partition("apt:///")[2] | 1332 | LOG.exception("show_available_packages: can not open %r, error:", |
495 | 1229 | elif packages and packages[0].startswith("apt://"): | 1333 | packages) |
496 | 1230 | packages[0] = packages[0].partition("apt://")[2] | 1334 | dialogs.error(None, |
467 | 1231 | elif packages and packages[0].startswith("apt:"): | ||
468 | 1232 | packages[0] = packages[0].partition("apt:")[2] | ||
469 | 1233 | |||
470 | 1234 | # allow s-c to be called with a search term | ||
471 | 1235 | if packages and packages[0].startswith("search:"): | ||
472 | 1236 | packages[0] = packages[0].partition("search:")[2] | ||
473 | 1237 | self.available_pane.init_view() | ||
474 | 1238 | self.available_pane.searchentry.set_text(" ".join(packages)) | ||
475 | 1239 | return | ||
476 | 1240 | |||
477 | 1241 | if len(packages) == 1: | ||
478 | 1242 | request = packages[0] | ||
479 | 1243 | |||
480 | 1244 | # are we dealing with a path? | ||
481 | 1245 | if os.path.exists(request) and not os.path.isdir(request): | ||
482 | 1246 | if not request.startswith('/'): | ||
483 | 1247 | # we may have been given a relative path | ||
484 | 1248 | request = os.path.join(os.getcwd(), request) | ||
485 | 1249 | try: | ||
486 | 1250 | app = DebFileApplication(request) | ||
487 | 1251 | except ValueError as e: | ||
488 | 1252 | LOG.error("can not open %s: %s" % (request, e)) | ||
489 | 1253 | from softwarecenter.ui.gtk3.dialogs import error | ||
490 | 1254 | error(None, | ||
497 | 1255 | _("Error"), | 1335 | _("Error"), |
525 | 1256 | _("The file “%s” could not be opened.") % request) | 1336 | _("The file “%s” could not be opened.") % e.path) |
526 | 1257 | app = None | 1337 | search_text = app = None |
527 | 1258 | else: | 1338 | |
528 | 1259 | # package from archive | 1339 | LOG.info('show_available_packages: search_text is %r, app is %r.', |
529 | 1260 | # if there is a "/" in the string consider it as tuple | 1340 | search_text, app) |
530 | 1261 | # of (pkgname, appname) for exact matching (used by | 1341 | |
531 | 1262 | # e.g. unity | 1342 | if search_text: |
505 | 1263 | (pkgname, sep, appname) = packages[0].partition("/") | ||
506 | 1264 | app = Application(appname, pkgname) | ||
507 | 1265 | |||
508 | 1266 | @wait_for_apt_cache_ready | ||
509 | 1267 | def show_app(self, app): | ||
510 | 1268 | # if the pkg is installed, show it in the installed pane | ||
511 | 1269 | if (app.pkgname in self.cache and | ||
512 | 1270 | self.cache[app.pkgname].installed): | ||
513 | 1271 | with ExecutionTime("installed_pane.init_view()"): | ||
514 | 1272 | self.installed_pane.init_view() | ||
515 | 1273 | with ExecutionTime("installed_pane.show_app()"): | ||
516 | 1274 | self.installed_pane.show_app(app) | ||
517 | 1275 | else: | ||
518 | 1276 | self.available_pane.init_view() | ||
519 | 1277 | self.available_pane.show_app(app) | ||
520 | 1278 | if app: | ||
521 | 1279 | show_app(self, app) | ||
522 | 1280 | return | ||
523 | 1281 | elif len(packages) > 1: | ||
524 | 1282 | # turn multiple packages into a search with "," | ||
532 | 1283 | self.available_pane.init_view() | 1343 | self.available_pane.init_view() |
538 | 1284 | self.available_pane.searchentry.set_text(",".join(packages)) | 1344 | self.available_pane.searchentry.set_text(search_text) |
539 | 1285 | return | 1345 | elif app is not None: |
540 | 1286 | # normal startup, show the lobby (it will have a spinner when | 1346 | self.show_app(app) |
541 | 1287 | # its not ready yet) - it will also initialize the view | 1347 | else: |
542 | 1288 | self.view_manager.set_active_view(ViewPages.AVAILABLE) | 1348 | # normal startup, show the lobby (it will have a spinner when |
543 | 1349 | # its not ready yet) - it will also initialize the view | ||
544 | 1350 | self.view_manager.set_active_view(ViewPages.AVAILABLE) | ||
545 | 1289 | 1351 | ||
546 | 1290 | def restore_state(self): | 1352 | def restore_state(self): |
547 | 1291 | if self.config.has_option("general", "size"): | 1353 | if self.config.has_option("general", "size"): |
548 | 1292 | 1354 | ||
549 | === modified file 'softwarecenter/ui/gtk3/models/appstore2.py' | |||
550 | --- softwarecenter/ui/gtk3/models/appstore2.py 2012-05-18 05:47:10 +0000 | |||
551 | +++ softwarecenter/ui/gtk3/models/appstore2.py 2012-05-21 15:37:20 +0000 | |||
552 | @@ -164,6 +164,7 @@ | |||
553 | 164 | if doc.installed is None: | 164 | if doc.installed is None: |
554 | 165 | pkgname = self.get_pkgname(doc) | 165 | pkgname = self.get_pkgname(doc) |
555 | 166 | doc.installed = (self.is_available(doc) and | 166 | doc.installed = (self.is_available(doc) and |
556 | 167 | pkgname in self.cache and | ||
557 | 167 | self.cache[pkgname].is_installed) | 168 | self.cache[pkgname].is_installed) |
558 | 168 | return doc.installed | 169 | return doc.installed |
559 | 169 | 170 | ||
560 | 170 | 171 | ||
561 | === modified file 'softwarecenter/ui/gtk3/session/appmanager.py' | |||
562 | --- softwarecenter/ui/gtk3/session/appmanager.py 2012-03-15 00:16:03 +0000 | |||
563 | +++ softwarecenter/ui/gtk3/session/appmanager.py 2012-05-21 15:37:20 +0000 | |||
564 | @@ -71,6 +71,11 @@ | |||
565 | 71 | else: | 71 | else: |
566 | 72 | _appmanager = self | 72 | _appmanager = self |
567 | 73 | 73 | ||
568 | 74 | def destroy(self): | ||
569 | 75 | """Destroy the global instance.""" | ||
570 | 76 | global _appmanager | ||
571 | 77 | _appmanager = None | ||
572 | 78 | |||
573 | 74 | def request_action(self, app, addons_install, addons_remove, action): | 79 | def request_action(self, app, addons_install, addons_remove, action): |
574 | 75 | """callback when an app action is requested from the appview, | 80 | """callback when an app action is requested from the appview, |
575 | 76 | if action is "remove", must check if other dependencies have to be | 81 | if action is "remove", must check if other dependencies have to be |
576 | 77 | 82 | ||
577 | === modified file 'softwarecenter/ui/gtk3/session/viewmanager.py' | |||
578 | --- softwarecenter/ui/gtk3/session/viewmanager.py 2012-03-15 00:16:03 +0000 | |||
579 | +++ softwarecenter/ui/gtk3/session/viewmanager.py 2012-05-21 15:37:20 +0000 | |||
580 | @@ -69,6 +69,11 @@ | |||
581 | 69 | else: | 69 | else: |
582 | 70 | _viewmanager = self | 70 | _viewmanager = self |
583 | 71 | 71 | ||
584 | 72 | def destroy(self): | ||
585 | 73 | """Destroy the global instance.""" | ||
586 | 74 | global _viewmanager | ||
587 | 75 | _viewmanager = None | ||
588 | 76 | |||
589 | 72 | def on_search_terms_changed(self, widget, new_text): | 77 | def on_search_terms_changed(self, widget, new_text): |
590 | 73 | pane = self.get_current_view_widget() | 78 | pane = self.get_current_view_widget() |
591 | 74 | if hasattr(pane, "on_search_terms_changed"): | 79 | if hasattr(pane, "on_search_terms_changed"): |
592 | 75 | 80 | ||
593 | === modified file 'softwarecenter/ui/gtk3/views/appdetailsview.py' | |||
594 | --- softwarecenter/ui/gtk3/views/appdetailsview.py 2012-05-14 03:16:07 +0000 | |||
595 | +++ softwarecenter/ui/gtk3/views/appdetailsview.py 2012-05-21 15:37:20 +0000 | |||
596 | @@ -1738,7 +1738,7 @@ | |||
597 | 1738 | # update all (but skip the addons calculation if this is a | 1738 | # update all (but skip the addons calculation if this is a |
598 | 1739 | # DebFileApplication as this is not useful for this case and it | 1739 | # DebFileApplication as this is not useful for this case and it |
599 | 1740 | # increases the view load time dramatically) | 1740 | # increases the view load time dramatically) |
601 | 1741 | skip_update_addons = type(self.app) == DebFileApplication | 1741 | skip_update_addons = isinstance(self.app, DebFileApplication) |
602 | 1742 | self._update_all(self.app_details, | 1742 | self._update_all(self.app_details, |
603 | 1743 | skip_update_addons=skip_update_addons) | 1743 | skip_update_addons=skip_update_addons) |
604 | 1744 | 1744 | ||
605 | @@ -2013,7 +2013,7 @@ | |||
606 | 2013 | self.addons_manager.addons_to_remove, | 2013 | self.addons_manager.addons_to_remove, |
607 | 2014 | self.app.archive_suite) | 2014 | self.app.archive_suite) |
608 | 2015 | total_download_size, total_install_size = res | 2015 | total_download_size, total_install_size = res |
610 | 2016 | if res == (0, 0) and type(self.app) == DebFileApplication: | 2016 | if res == (0, 0) and isinstance(self.app, DebFileApplication): |
611 | 2017 | total_install_size = self.app_details.installed_size | 2017 | total_install_size = self.app_details.installed_size |
612 | 2018 | if total_download_size > 0: | 2018 | if total_download_size > 0: |
613 | 2019 | download_size = GLib.format_size(total_download_size) | 2019 | download_size = GLib.format_size(total_download_size) |
614 | 2020 | 2020 | ||
615 | === modified file 'softwarecenter/ui/gtk3/widgets/videoplayer.py' | |||
616 | --- softwarecenter/ui/gtk3/widgets/videoplayer.py 2012-03-08 17:42:36 +0000 | |||
617 | +++ softwarecenter/ui/gtk3/widgets/videoplayer.py 2012-05-21 15:37:20 +0000 | |||
618 | @@ -72,7 +72,11 @@ | |||
619 | 72 | # uri property | 72 | # uri property |
620 | 73 | def _set_uri(self, v): | 73 | def _set_uri(self, v): |
621 | 74 | self._uri = v or "" | 74 | self._uri = v or "" |
623 | 75 | self.webkit.load_uri(self._uri) | 75 | if self._uri: |
624 | 76 | # only load the uri if it's defined, otherwise we may get: | ||
625 | 77 | # Program received signal SIGSEGV, Segmentation fault. | ||
626 | 78 | # webkit_web_frame_load_uri () from /usr/lib/libwebkitgtk-3.0.so.0 | ||
627 | 79 | self.webkit.load_uri(self._uri) | ||
628 | 76 | 80 | ||
629 | 77 | def _get_uri(self): | 81 | def _get_uri(self): |
630 | 78 | return self._uri | 82 | return self._uri |
631 | 79 | 83 | ||
632 | === added file 'test/gtk3/__init__.py' | |||
633 | === added file 'test/gtk3/test_app.py' | |||
634 | --- test/gtk3/test_app.py 1970-01-01 00:00:00 +0000 | |||
635 | +++ test/gtk3/test_app.py 2012-05-21 15:37:20 +0000 | |||
636 | @@ -0,0 +1,357 @@ | |||
637 | 1 | #!/usr/bin/python | ||
638 | 2 | |||
639 | 3 | import os | ||
640 | 4 | import unittest | ||
641 | 5 | |||
642 | 6 | from collections import defaultdict | ||
643 | 7 | from functools import partial | ||
644 | 8 | |||
645 | 9 | from mock import Mock | ||
646 | 10 | |||
647 | 11 | from testutils import get_mock_options, setup_test_env | ||
648 | 12 | setup_test_env() | ||
649 | 13 | |||
650 | 14 | import softwarecenter.paths | ||
651 | 15 | from softwarecenter.db import DebFileApplication, DebFileOpenError | ||
652 | 16 | from softwarecenter.enums import PkgStates, SearchSeparators | ||
653 | 17 | from softwarecenter.ui.gtk3 import app | ||
654 | 18 | |||
655 | 19 | |||
656 | 20 | class FakedCache(dict): | ||
657 | 21 | """A faked cache.""" | ||
658 | 22 | |||
659 | 23 | def __init__(self, *a, **kw): | ||
660 | 24 | super(FakedCache, self).__init__() | ||
661 | 25 | self._callbacks = defaultdict(list) | ||
662 | 26 | self.ready = False | ||
663 | 27 | |||
664 | 28 | def open(self): | ||
665 | 29 | """Open this cache.""" | ||
666 | 30 | self.ready = True | ||
667 | 31 | |||
668 | 32 | def connect(self, signal, callback): | ||
669 | 33 | """Connect a signal with a callback.""" | ||
670 | 34 | self._callbacks[signal].append(callback) | ||
671 | 35 | |||
672 | 36 | def disconnect_by_func(self, callback): | ||
673 | 37 | """Disconnect 'callback' from every signal.""" | ||
674 | 38 | for signal, cb in self._callbacks.iteritems(): | ||
675 | 39 | if cb == callback: | ||
676 | 40 | self._callbacks[signal].remove(callback) | ||
677 | 41 | if len(self._callbacks[signal]) == 0: | ||
678 | 42 | self._callbacks.pop(signal) | ||
679 | 43 | |||
680 | 44 | def get_addons(self, pkgname): | ||
681 | 45 | """Return (recommended, suggested) addons for 'pkgname'.""" | ||
682 | 46 | return ([],[]) | ||
683 | 47 | |||
684 | 48 | def get_total_size_on_install(self,pkgname, addons_to_install, | ||
685 | 49 | addons_to_remove, archive_suite): | ||
686 | 50 | """Return a fake (total_download_size, total_install_size) result.""" | ||
687 | 51 | return (0, 0) | ||
688 | 52 | |||
689 | 53 | |||
690 | 54 | class ParsePackagesArgsTestCase(unittest.TestCase): | ||
691 | 55 | """Test suite for the parse_packages_args helper.""" | ||
692 | 56 | |||
693 | 57 | pkg_name = 'foo' | ||
694 | 58 | |||
695 | 59 | def transform_for_test(self, items): | ||
696 | 60 | """Transform a sequence into a comma separated string.""" | ||
697 | 61 | return app.SEARCH_PREFIX + SearchSeparators.REGULAR.join(items) | ||
698 | 62 | |||
699 | 63 | def do_check(self, apps, items=None): | ||
700 | 64 | """Check that the available_pane was shown.""" | ||
701 | 65 | if items is None: | ||
702 | 66 | items = self.transform_for_test(apps) | ||
703 | 67 | |||
704 | 68 | search_text, result_app = app.parse_packages_args(items) | ||
705 | 69 | |||
706 | 70 | self.assertEqual(SearchSeparators.REGULAR.join(apps), search_text) | ||
707 | 71 | self.assertIsNone(result_app) | ||
708 | 72 | |||
709 | 73 | def test_empty(self): | ||
710 | 74 | """Pass an empty argument, show the 'available' view.""" | ||
711 | 75 | self.do_check(apps=()) | ||
712 | 76 | |||
713 | 77 | def test_single_empty_item(self): | ||
714 | 78 | """Pass a single empty item, show the 'available' view.""" | ||
715 | 79 | self.do_check(apps=('',)) | ||
716 | 80 | |||
717 | 81 | def test_single_item(self): | ||
718 | 82 | """Pass a single item, show the 'available' view.""" | ||
719 | 83 | self.do_check(apps=(self.pkg_name,)) | ||
720 | 84 | |||
721 | 85 | def test_two_items(self): | ||
722 | 86 | """Pass two items, show the 'available' view.""" | ||
723 | 87 | self.do_check(apps=(self.pkg_name, 'bar')) | ||
724 | 88 | |||
725 | 89 | def test_several_items(self): | ||
726 | 90 | """Pass several items, show the 'available' view.""" | ||
727 | 91 | self.do_check(apps=(self.pkg_name, 'firefox', 'software-center')) | ||
728 | 92 | |||
729 | 93 | |||
730 | 94 | class ParsePackageArgsAsFileTestCase(unittest.TestCase): | ||
731 | 95 | |||
732 | 96 | def test_item_is_a_file(self): | ||
733 | 97 | """Pass an item that is an existing file.""" | ||
734 | 98 | # pass a real deb here | ||
735 | 99 | fname = os.path.join(os.path.dirname(os.path.abspath(__file__)), | ||
736 | 100 | "..", "data", "test_debs", "gdebi-test1.deb") | ||
737 | 101 | assert os.path.exists(fname) | ||
738 | 102 | # test once as string and as list | ||
739 | 103 | for items in ( fname, [fname] ): | ||
740 | 104 | search_text, result_app = app.parse_packages_args(fname) | ||
741 | 105 | self.assertIsInstance(result_app, DebFileApplication) | ||
742 | 106 | |||
743 | 107 | def test_item_is_invalid_file(self): | ||
744 | 108 | """ Pass an invalid file item """ | ||
745 | 109 | fname = __file__ | ||
746 | 110 | assert os.path.exists(fname) | ||
747 | 111 | self.assertRaises(DebFileOpenError, app.parse_packages_args, fname) | ||
748 | 112 | |||
749 | 113 | |||
750 | 114 | class ParsePackagesWithAptPrefixTestCase(ParsePackagesArgsTestCase): | ||
751 | 115 | |||
752 | 116 | installed = None | ||
753 | 117 | |||
754 | 118 | def setUp(self): | ||
755 | 119 | super(ParsePackagesWithAptPrefixTestCase, self).setUp() | ||
756 | 120 | |||
757 | 121 | self.cache = FakedCache() | ||
758 | 122 | self.db = app.StoreDatabase(cache=self.cache) | ||
759 | 123 | |||
760 | 124 | assert self.pkg_name not in self.cache | ||
761 | 125 | if self.installed is not None: | ||
762 | 126 | mock_cache_entry = Mock() | ||
763 | 127 | mock_cache_entry.website = None | ||
764 | 128 | mock_cache_entry.license = None | ||
765 | 129 | mock_cache_entry.installed_files = [] | ||
766 | 130 | mock_cache_entry.candidate = Mock() | ||
767 | 131 | mock_cache_entry.candidate.version = '1.0' | ||
768 | 132 | mock_cache_entry.candidate.description = 'A nonsense app.' | ||
769 | 133 | mock_cache_entry.candidate.origins = () | ||
770 | 134 | mock_cache_entry.versions = (Mock(),) | ||
771 | 135 | mock_cache_entry.versions[0].version = '0.99' | ||
772 | 136 | mock_cache_entry.versions[0].origins = (Mock(),) | ||
773 | 137 | mock_cache_entry.versions[0].origins[0].archive = 'test' | ||
774 | 138 | mock_cache_entry.is_installed = self.installed | ||
775 | 139 | if self.installed: | ||
776 | 140 | mock_cache_entry.installed = Mock() | ||
777 | 141 | mock_cache_entry.installed.version = '0.90' | ||
778 | 142 | mock_cache_entry.installed.installed_size = 0 | ||
779 | 143 | else: | ||
780 | 144 | mock_cache_entry.installed = None | ||
781 | 145 | |||
782 | 146 | self.cache[self.pkg_name] = mock_cache_entry | ||
783 | 147 | self.addCleanup(self.cache.pop, self.pkg_name) | ||
784 | 148 | |||
785 | 149 | |||
786 | 150 | def transform_for_test(self, items): | ||
787 | 151 | """Do nothing.""" | ||
788 | 152 | return items | ||
789 | 153 | |||
790 | 154 | def check_package_availability(self, name): | ||
791 | 155 | """Check whether the package 'name' is available.""" | ||
792 | 156 | if name not in self.cache: | ||
793 | 157 | state = PkgStates.NOT_FOUND | ||
794 | 158 | elif self.cache[name].installed: | ||
795 | 159 | state = PkgStates.INSTALLED | ||
796 | 160 | else: | ||
797 | 161 | state = PkgStates.UNINSTALLED | ||
798 | 162 | return state | ||
799 | 163 | |||
800 | 164 | def do_check(self, apps, items=None): | ||
801 | 165 | """Check that the available_pane was shown.""" | ||
802 | 166 | if items is None: | ||
803 | 167 | items = self.transform_for_test(apps) | ||
804 | 168 | |||
805 | 169 | search_text, result_app = app.parse_packages_args(items) | ||
806 | 170 | |||
807 | 171 | if apps and len(apps) == 1 and apps[0] and not os.path.isfile(apps[0]): | ||
808 | 172 | self.assertIsNotNone(result_app) | ||
809 | 173 | app_details = result_app.get_details(self.db) | ||
810 | 174 | |||
811 | 175 | self.assertEqual(apps[0], app_details.name) | ||
812 | 176 | state = self.check_package_availability(app_details.name) | ||
813 | 177 | self.assertEqual(state, app_details.pkg_state) | ||
814 | 178 | else: | ||
815 | 179 | self.assertIsNone(result_app) | ||
816 | 180 | |||
817 | 181 | if apps and (len(apps) > 1 or os.path.isfile(apps[0])): | ||
818 | 182 | self.assertEqual(SearchSeparators.PACKAGE.join(apps), search_text) | ||
819 | 183 | else: | ||
820 | 184 | self.assertEqual('', search_text) | ||
821 | 185 | |||
822 | 186 | def test_item_with_prefix(self): | ||
823 | 187 | """Pass a item with the item prefix.""" | ||
824 | 188 | for prefix in ('apt:', 'apt://', 'apt:///'): | ||
825 | 189 | for case in (self.pkg_name, app.PACKAGE_PREFIX + self.pkg_name): | ||
826 | 190 | self.do_check(apps=(case,), items=(prefix + case,)) | ||
827 | 191 | |||
828 | 192 | |||
829 | 193 | class ParsePackagesNotInstalledTestCase(ParsePackagesWithAptPrefixTestCase): | ||
830 | 194 | """Test suite for parsing/searching/loading package lists.""" | ||
831 | 195 | |||
832 | 196 | installed = False | ||
833 | 197 | |||
834 | 198 | |||
835 | 199 | class ParsePackagesInstalledTestCase(ParsePackagesWithAptPrefixTestCase): | ||
836 | 200 | """Test suite for parsing/searching/loading package lists.""" | ||
837 | 201 | |||
838 | 202 | installed = True | ||
839 | 203 | |||
840 | 204 | |||
841 | 205 | class ParsePackagesArgsStringTestCase(ParsePackagesWithAptPrefixTestCase): | ||
842 | 206 | """Test suite for parsing/loading package lists from strings.""" | ||
843 | 207 | |||
844 | 208 | def transform_for_test(self, items): | ||
845 | 209 | """Transform a sequence into a comma separated string.""" | ||
846 | 210 | return SearchSeparators.PACKAGE.join(items) | ||
847 | 211 | |||
848 | 212 | |||
849 | 213 | class AppTestCase(unittest.TestCase): | ||
850 | 214 | """Test suite for the app module.""" | ||
851 | 215 | |||
852 | 216 | def setUp(self): | ||
853 | 217 | super(AppTestCase, self).setUp() | ||
854 | 218 | self.called = defaultdict(list) | ||
855 | 219 | self.addCleanup(self.called.clear) | ||
856 | 220 | |||
857 | 221 | orig = app.SoftwareCenterAppGtk3.START_DBUS | ||
858 | 222 | self.addCleanup(setattr, app.SoftwareCenterAppGtk3, 'START_DBUS', orig) | ||
859 | 223 | app.SoftwareCenterAppGtk3.START_DBUS = False | ||
860 | 224 | |||
861 | 225 | orig = app.get_pkg_info | ||
862 | 226 | self.addCleanup(setattr, app, 'get_pkg_info', orig) | ||
863 | 227 | app.get_pkg_info = lambda: FakedCache() | ||
864 | 228 | |||
865 | 229 | datadir = softwarecenter.paths.datadir | ||
866 | 230 | xapianpath = softwarecenter.paths.XAPIAN_BASE_PATH | ||
867 | 231 | options = get_mock_options() | ||
868 | 232 | self.app = app.SoftwareCenterAppGtk3(datadir, xapianpath, options) | ||
869 | 233 | self.addCleanup(self.app.destroy) | ||
870 | 234 | |||
871 | 235 | self.app.cache.open() | ||
872 | 236 | |||
873 | 237 | # connect some signals of interest | ||
874 | 238 | cid = self.app.installed_pane.connect('installed-pane-created', | ||
875 | 239 | partial(self.track_calls, 'pane-created')) | ||
876 | 240 | self.addCleanup(self.app.installed_pane.disconnect, cid) | ||
877 | 241 | |||
878 | 242 | cid = self.app.available_pane.connect('available-pane-created', | ||
879 | 243 | partial(self.track_calls, 'pane-created')) | ||
880 | 244 | self.addCleanup(self.app.available_pane.disconnect, cid) | ||
881 | 245 | |||
882 | 246 | def track_calls(self, name, *a, **kw): | ||
883 | 247 | """Record the callback for 'name' using 'args' and 'kwargs'.""" | ||
884 | 248 | self.called[name].append((a, kw)) | ||
885 | 249 | |||
886 | 250 | |||
887 | 251 | class ShowPackagesTestCase(AppTestCase): | ||
888 | 252 | """Test suite for parsing/searching/loading package lists.""" | ||
889 | 253 | |||
890 | 254 | def do_check(self, packages, search_text): | ||
891 | 255 | """Check that the available_pane was shown.""" | ||
892 | 256 | self.app.show_available_packages(packages=packages) | ||
893 | 257 | |||
894 | 258 | self.assertEqual(self.called, | ||
895 | 259 | {'pane-created': [((self.app.available_pane,), {})]}) | ||
896 | 260 | |||
897 | 261 | actual = self.app.available_pane.searchentry.get_text() | ||
898 | 262 | self.assertEqual(search_text, actual, | ||
899 | 263 | 'Expected search text %r (got %r instead) for packages %r.' % | ||
900 | 264 | (search_text, actual, packages)) | ||
901 | 265 | |||
902 | 266 | self.assertIsNone(self.app.available_pane.app_details_view.app_details) | ||
903 | 267 | |||
904 | 268 | def test_show_available_packages_search_prefix(self): | ||
905 | 269 | """Check that the available_pane was shown.""" | ||
906 | 270 | self.do_check(packages='search:foo,bar baz', search_text='foo bar baz') | ||
907 | 271 | |||
908 | 272 | def test_show_available_packages_apt_prefix(self): | ||
909 | 273 | """Check that the available_pane was shown.""" | ||
910 | 274 | for prefix in ('apt:', 'apt://', 'apt:///'): | ||
911 | 275 | self.do_check(packages=prefix + 'foo,bar,baz', | ||
912 | 276 | search_text='foo,bar,baz') | ||
913 | 277 | |||
914 | 278 | |||
915 | 279 | class ShowPackagesOnePackageTestCase(AppTestCase): | ||
916 | 280 | """Test suite for parsing/searching/loading package lists.""" | ||
917 | 281 | |||
918 | 282 | pkg_name = 'foo' | ||
919 | 283 | installed = None | ||
920 | 284 | |||
921 | 285 | def setUp(self): | ||
922 | 286 | super(ShowPackagesOnePackageTestCase, self).setUp() | ||
923 | 287 | assert self.pkg_name not in self.app.cache | ||
924 | 288 | if self.installed is not None: | ||
925 | 289 | mock_cache_entry = Mock() | ||
926 | 290 | mock_cache_entry.website = None | ||
927 | 291 | mock_cache_entry.license = None | ||
928 | 292 | mock_cache_entry.installed_files = [] | ||
929 | 293 | mock_cache_entry.candidate = Mock() | ||
930 | 294 | mock_cache_entry.candidate.version = '1.0' | ||
931 | 295 | mock_cache_entry.candidate.description = 'A nonsense app.' | ||
932 | 296 | mock_cache_entry.candidate.origins = () | ||
933 | 297 | mock_cache_entry.versions = (Mock(),) | ||
934 | 298 | mock_cache_entry.versions[0].version = '0.99' | ||
935 | 299 | mock_cache_entry.versions[0].origins = (Mock(),) | ||
936 | 300 | mock_cache_entry.versions[0].origins[0].archive = 'test' | ||
937 | 301 | mock_cache_entry.is_installed = self.installed | ||
938 | 302 | if self.installed: | ||
939 | 303 | mock_cache_entry.installed = Mock() | ||
940 | 304 | mock_cache_entry.installed.version = '0.90' | ||
941 | 305 | mock_cache_entry.installed.installed_size = 0 | ||
942 | 306 | else: | ||
943 | 307 | mock_cache_entry.installed = None | ||
944 | 308 | |||
945 | 309 | self.app.cache[self.pkg_name] = mock_cache_entry | ||
946 | 310 | self.addCleanup(self.app.cache.pop, self.pkg_name) | ||
947 | 311 | |||
948 | 312 | def check_package_availability(self, name): | ||
949 | 313 | """Check whether the package 'name' is available.""" | ||
950 | 314 | pane = self.app.available_pane | ||
951 | 315 | if name not in self.app.cache: | ||
952 | 316 | state = PkgStates.NOT_FOUND | ||
953 | 317 | elif self.app.cache[name].installed: | ||
954 | 318 | state = PkgStates.INSTALLED | ||
955 | 319 | pane = self.app.installed_pane | ||
956 | 320 | else: | ||
957 | 321 | state = PkgStates.UNINSTALLED | ||
958 | 322 | |||
959 | 323 | self.assertEqual(state, pane.app_details_view.app_details.pkg_state) | ||
960 | 324 | |||
961 | 325 | return pane | ||
962 | 326 | |||
963 | 327 | def test_show_available_packages(self): | ||
964 | 328 | """Check that the available_pane was shown.""" | ||
965 | 329 | self.app.show_available_packages(packages=self.pkg_name) | ||
966 | 330 | |||
967 | 331 | expected_pane = self.check_package_availability(self.pkg_name) | ||
968 | 332 | name = expected_pane.app_details_view.app_details.name | ||
969 | 333 | self.assertEqual(self.pkg_name, name) | ||
970 | 334 | |||
971 | 335 | self.assertEqual('', self.app.available_pane.searchentry.get_text()) | ||
972 | 336 | |||
973 | 337 | self.assertEqual(self.called, | ||
974 | 338 | {'pane-created': [((expected_pane,), {})]}) | ||
975 | 339 | |||
976 | 340 | |||
977 | 341 | class ShowPackagesNotInstalledTestCase(ShowPackagesOnePackageTestCase): | ||
978 | 342 | """Test suite for parsing/searching/loading package lists.""" | ||
979 | 343 | |||
980 | 344 | installed = False | ||
981 | 345 | |||
982 | 346 | |||
983 | 347 | class ShowPackagesInstalledTestCase(ShowPackagesOnePackageTestCase): | ||
984 | 348 | """Test suite for parsing/searching/loading package lists.""" | ||
985 | 349 | |||
986 | 350 | installed = True | ||
987 | 351 | |||
988 | 352 | |||
989 | 353 | if __name__ == "__main__": | ||
990 | 354 | # avoid spawning recommender-agent, reviews, software-center-agent etc, | ||
991 | 355 | # cuts ~5s or so | ||
992 | 356 | os.environ["SOFTWARE_CENTER_DISABLE_SPAWN_HELPER"] = "1" | ||
993 | 357 | unittest.main() | ||
994 | 0 | 358 | ||
995 | === modified file 'test/gtk3/test_debfile_view.py' | |||
996 | --- test/gtk3/test_debfile_view.py 2012-02-23 16:40:27 +0000 | |||
997 | +++ test/gtk3/test_debfile_view.py 2012-05-21 15:37:20 +0000 | |||
998 | @@ -3,22 +3,18 @@ | |||
999 | 3 | import time | 3 | import time |
1000 | 4 | import unittest | 4 | import unittest |
1001 | 5 | 5 | ||
1005 | 6 | from mock import Mock | 6 | from testutils import do_events, get_mock_options, setup_test_env |
1003 | 7 | |||
1004 | 8 | from testutils import setup_test_env, do_events | ||
1006 | 9 | setup_test_env() | 7 | setup_test_env() |
1007 | 10 | 8 | ||
1008 | 11 | import softwarecenter.paths | 9 | import softwarecenter.paths |
1009 | 12 | from softwarecenter.ui.gtk3.app import SoftwareCenterAppGtk3 | 10 | from softwarecenter.ui.gtk3.app import SoftwareCenterAppGtk3 |
1010 | 13 | from softwarecenter.ui.gtk3.panes.availablepane import AvailablePane | 11 | from softwarecenter.ui.gtk3.panes.availablepane import AvailablePane |
1011 | 14 | 12 | ||
1012 | 13 | |||
1013 | 15 | class DebFileOpenTestCase(unittest.TestCase): | 14 | class DebFileOpenTestCase(unittest.TestCase): |
1014 | 16 | 15 | ||
1015 | 17 | def test_deb_file_view_error(self): | 16 | def test_deb_file_view_error(self): |
1020 | 18 | mock_options = Mock() | 17 | mock_options = get_mock_options() |
1017 | 19 | mock_options.display_navlog = False | ||
1018 | 20 | mock_options.disable_apt_xapian_index = False | ||
1019 | 21 | mock_options.disable_buy = False | ||
1021 | 22 | xapianpath = softwarecenter.paths.XAPIAN_BASE_PATH | 18 | xapianpath = softwarecenter.paths.XAPIAN_BASE_PATH |
1022 | 23 | app = SoftwareCenterAppGtk3( | 19 | app = SoftwareCenterAppGtk3( |
1023 | 24 | softwarecenter.paths.datadir, xapianpath, mock_options) | 20 | softwarecenter.paths.datadir, xapianpath, mock_options) |
1024 | @@ -37,7 +33,6 @@ | |||
1025 | 37 | # this is deb that is not installable | 33 | # this is deb that is not installable |
1026 | 38 | action_button = app.available_pane.app_details_view.pkg_statusbar.button | 34 | action_button = app.available_pane.app_details_view.pkg_statusbar.button |
1027 | 39 | self.assertFalse(action_button.get_property("visible")) | 35 | self.assertFalse(action_button.get_property("visible")) |
1028 | 40 | |||
1029 | 41 | 36 | ||
1030 | 42 | 37 | ||
1031 | 43 | if __name__ == "__main__": | 38 | if __name__ == "__main__": |
1032 | 44 | 39 | ||
1033 | === modified file 'test/gtk3/test_purchase.py' | |||
1034 | --- test/gtk3/test_purchase.py 2012-03-19 14:38:10 +0000 | |||
1035 | +++ test/gtk3/test_purchase.py 2012-05-21 15:37:20 +0000 | |||
1036 | @@ -3,12 +3,12 @@ | |||
1037 | 3 | import time | 3 | import time |
1038 | 4 | import unittest | 4 | import unittest |
1039 | 5 | 5 | ||
1041 | 6 | from mock import Mock,patch | 6 | from mock import Mock, patch |
1042 | 7 | 7 | ||
1043 | 8 | from testutils import setup_test_env | 8 | from testutils import setup_test_env |
1044 | 9 | setup_test_env() | 9 | setup_test_env() |
1045 | 10 | 10 | ||
1047 | 11 | from softwarecenter.testutils import do_events | 11 | from softwarecenter.testutils import do_events, get_mock_options |
1048 | 12 | from softwarecenter.ui.gtk3.app import SoftwareCenterAppGtk3 | 12 | from softwarecenter.ui.gtk3.app import SoftwareCenterAppGtk3 |
1049 | 13 | from softwarecenter.ui.gtk3.panes.availablepane import AvailablePane | 13 | from softwarecenter.ui.gtk3.panes.availablepane import AvailablePane |
1050 | 14 | import softwarecenter.paths | 14 | import softwarecenter.paths |
1051 | @@ -35,7 +35,7 @@ | |||
1052 | 35 | self.assertTrue("skipping" in mock.debug.call_args[0][0]) | 35 | self.assertTrue("skipping" in mock.debug.call_args[0][0]) |
1053 | 36 | self.assertFalse("consumer_secret" in mock.debug.call_args[0][0]) | 36 | self.assertFalse("consumer_secret" in mock.debug.call_args[0][0]) |
1054 | 37 | mock.reset_mock() | 37 | mock.reset_mock() |
1056 | 38 | 38 | ||
1057 | 39 | # run another one | 39 | # run another one |
1058 | 40 | win.destroy() | 40 | win.destroy() |
1059 | 41 | 41 | ||
1060 | @@ -71,25 +71,21 @@ | |||
1061 | 71 | # run another one | 71 | # run another one |
1062 | 72 | win.destroy() | 72 | win.destroy() |
1063 | 73 | 73 | ||
1064 | 74 | |||
1065 | 75 | def test_reinstall_previous_purchase_display(self): | 74 | def test_reinstall_previous_purchase_display(self): |
1070 | 76 | mock_options = Mock() | 75 | mock_options = get_mock_options() |
1067 | 77 | mock_options.display_navlog = False | ||
1068 | 78 | mock_options.disable_apt_xapian_index = False | ||
1069 | 79 | mock_options.disable_buy = False | ||
1071 | 80 | xapiandb = "/var/cache/software-center/" | 76 | xapiandb = "/var/cache/software-center/" |
1072 | 81 | app = SoftwareCenterAppGtk3( | 77 | app = SoftwareCenterAppGtk3( |
1073 | 82 | softwarecenter.paths.datadir, xapiandb, mock_options) | 78 | softwarecenter.paths.datadir, xapiandb, mock_options) |
1077 | 83 | # real app opens cache async | 79 | # real app opens cache async |
1078 | 84 | app.cache.open() | 80 | app.cache.open() |
1079 | 85 | # show it | 81 | # show it |
1080 | 86 | app.window_main.show_all() | 82 | app.window_main.show_all() |
1081 | 87 | app.available_pane.init_view() | 83 | app.available_pane.init_view() |
1082 | 88 | self._p() | 84 | self._p() |
1083 | 89 | app.on_menuitem_reinstall_purchases_activate(None) | 85 | app.on_menuitem_reinstall_purchases_activate(None) |
1084 | 90 | # it can take a bit until the sso client is ready | 86 | # it can take a bit until the sso client is ready |
1085 | 91 | for i in range(100): | 87 | for i in range(100): |
1087 | 92 | if (app.available_pane.get_current_page() == | 88 | if (app.available_pane.get_current_page() == |
1088 | 93 | AvailablePane.Pages.LIST): | 89 | AvailablePane.Pages.LIST): |
1089 | 94 | break | 90 | break |
1090 | 95 | self._p() | 91 | self._p() |
1091 | 96 | 92 | ||
1092 | === modified file 'test/test_database.py' | |||
1093 | --- test/test_database.py 2012-04-13 16:32:26 +0000 | |||
1094 | +++ test/test_database.py 2012-05-21 15:37:20 +0000 | |||
1095 | @@ -406,9 +406,13 @@ | |||
1096 | 406 | packagesize 3 # package size | 406 | packagesize 3 # package size |
1097 | 407 | app-popcon 4 # app-install .desktop popcon rank | 407 | app-popcon 4 # app-install .desktop popcon rank |
1098 | 408 | """ | 408 | """ |
1100 | 409 | open("axi-test-values","w").write(s) | 409 | fname = "axi-test-values" |
1101 | 410 | with open(fname, "w") as f: | ||
1102 | 411 | f.write(s) | ||
1103 | 412 | self.addCleanup(os.remove, fname) | ||
1104 | 413 | |||
1105 | 410 | #db = StoreDatabase("/var/cache/software-center/xapian", self.cache) | 414 | #db = StoreDatabase("/var/cache/software-center/xapian", self.cache) |
1107 | 411 | axi_values = parse_axi_values_file("axi-test-values") | 415 | axi_values = parse_axi_values_file(fname) |
1108 | 412 | self.assertNotEqual(axi_values, {}) | 416 | self.assertNotEqual(axi_values, {}) |
1109 | 413 | print axi_values | 417 | print axi_values |
1110 | 414 | 418 | ||
1111 | @@ -428,7 +432,8 @@ | |||
1112 | 428 | nonblocking_load=False) | 432 | nonblocking_load=False) |
1113 | 429 | self.assertTrue(len(enquirer.get_docids()) > 0) | 433 | self.assertTrue(len(enquirer.get_docids()) > 0) |
1114 | 430 | # FIXME: test more of the interface | 434 | # FIXME: test more of the interface |
1116 | 431 | 435 | ||
1117 | 436 | |||
1118 | 432 | class UtilsTestCase(unittest.TestCase): | 437 | class UtilsTestCase(unittest.TestCase): |
1119 | 433 | 438 | ||
1120 | 434 | def test_utils_get_installed_package_list(self): | 439 | def test_utils_get_installed_package_list(self): |
1121 | 435 | 440 | ||
1122 | === modified file 'test/test_debfileapplication.py' | |||
1123 | --- test/test_debfileapplication.py 2012-02-23 16:40:27 +0000 | |||
1124 | +++ test/test_debfileapplication.py 2012-05-21 15:37:20 +0000 | |||
1125 | @@ -7,7 +7,7 @@ | |||
1126 | 7 | setup_test_env() | 7 | setup_test_env() |
1127 | 8 | 8 | ||
1128 | 9 | from softwarecenter.enums import PkgStates | 9 | from softwarecenter.enums import PkgStates |
1130 | 10 | from softwarecenter.db.debfile import DebFileApplication | 10 | from softwarecenter.db.debfile import DebFileApplication, DebFileOpenError |
1131 | 11 | from softwarecenter.testutils import get_test_db | 11 | from softwarecenter.testutils import get_test_db |
1132 | 12 | 12 | ||
1133 | 13 | DEBFILE_PATH = './data/test_debs/gdebi-test9.deb' | 13 | DEBFILE_PATH = './data/test_debs/gdebi-test9.deb' |
1134 | @@ -22,6 +22,7 @@ | |||
1135 | 22 | DEBFILE_PATH_CORRUPT = './data/test_debs/corrupt.deb' | 22 | DEBFILE_PATH_CORRUPT = './data/test_debs/corrupt.deb' |
1136 | 23 | DEBFILE_NOT_INSTALLABLE = './data/test_debs/gdebi-test1.deb' | 23 | DEBFILE_NOT_INSTALLABLE = './data/test_debs/gdebi-test1.deb' |
1137 | 24 | 24 | ||
1138 | 25 | |||
1139 | 25 | class TestDebFileApplication(unittest.TestCase): | 26 | class TestDebFileApplication(unittest.TestCase): |
1140 | 26 | """ Test the class DebFileApplication """ | 27 | """ Test the class DebFileApplication """ |
1141 | 27 | 28 | ||
1142 | @@ -31,7 +32,7 @@ | |||
1143 | 31 | def test_get_name(self): | 32 | def test_get_name(self): |
1144 | 32 | debfileapplication = DebFileApplication(DEBFILE_PATH) | 33 | debfileapplication = DebFileApplication(DEBFILE_PATH) |
1145 | 33 | debfiledetails = debfileapplication.get_details(self.db) | 34 | debfiledetails = debfileapplication.get_details(self.db) |
1147 | 34 | 35 | ||
1148 | 35 | self.assertEquals(debfiledetails.name, DEBFILE_NAME) | 36 | self.assertEquals(debfiledetails.name, DEBFILE_NAME) |
1149 | 36 | 37 | ||
1150 | 37 | def test_get_description(self): | 38 | def test_get_description(self): |
1151 | @@ -64,9 +65,10 @@ | |||
1152 | 64 | debfileapplication = DebFileApplication(DEBFILE_PATH_NOTFOUND) | 65 | debfileapplication = DebFileApplication(DEBFILE_PATH_NOTFOUND) |
1153 | 65 | debfiledetails = debfileapplication.get_details(self.db) | 66 | debfiledetails = debfileapplication.get_details(self.db) |
1154 | 66 | self.assertEquals(debfiledetails.pkg_state, PkgStates.NOT_FOUND) | 67 | self.assertEquals(debfiledetails.pkg_state, PkgStates.NOT_FOUND) |
1156 | 67 | 68 | ||
1157 | 68 | def test_get_pkg_state_not_a_deb(self): | 69 | def test_get_pkg_state_not_a_deb(self): |
1159 | 69 | self.assertRaises(ValueError, DebFileApplication, DEBFILE_PATH_NOTADEB) | 70 | self.assertRaises(DebFileOpenError, |
1160 | 71 | DebFileApplication, DEBFILE_PATH_NOTADEB) | ||
1161 | 70 | 72 | ||
1162 | 71 | def test_get_pkg_state_corrupt(self): | 73 | def test_get_pkg_state_corrupt(self): |
1163 | 72 | debfileapplication = DebFileApplication(DEBFILE_PATH_CORRUPT) | 74 | debfileapplication = DebFileApplication(DEBFILE_PATH_CORRUPT) |
1164 | @@ -87,12 +89,14 @@ | |||
1165 | 87 | debfileapplication = DebFileApplication(DEBFILE_PATH) | 89 | debfileapplication = DebFileApplication(DEBFILE_PATH) |
1166 | 88 | debfiledetails = debfileapplication.get_details(self.db) | 90 | debfiledetails = debfileapplication.get_details(self.db) |
1167 | 89 | self.assertEquals(debfiledetails.installed_size, 0) | 91 | self.assertEquals(debfiledetails.installed_size, 0) |
1169 | 90 | 92 | ||
1170 | 91 | def test_get_warning(self): | 93 | def test_get_warning(self): |
1171 | 92 | debfileapplication = DebFileApplication(DEBFILE_PATH) | 94 | debfileapplication = DebFileApplication(DEBFILE_PATH) |
1172 | 93 | debfiledetails = debfileapplication.get_details(self.db) | 95 | debfiledetails = debfileapplication.get_details(self.db) |
1173 | 94 | self.assertEquals(debfiledetails.warning, DEBFILE_WARNING) | 96 | self.assertEquals(debfiledetails.warning, DEBFILE_WARNING) |
1175 | 95 | 97 | ||
1176 | 98 | |||
1177 | 96 | if __name__ == "__main__": | 99 | if __name__ == "__main__": |
1178 | 97 | logging.basicConfig(level=logging.DEBUG) | 100 | logging.basicConfig(level=logging.DEBUG) |
1179 | 98 | unittest.main() | 101 | unittest.main() |
1180 | 102 |
Thanks Natalia for this branch and sorry for my slow reply.
First let me say thanks for the work and the refactor and the excellent test, we shall use that
as a example.
This looks fine, there have been some changes in the 5.2 branch so I merged it and resolved
conflicts and tweaked it a bit more, please remerge lp:~mvo/software-center/fix-977931/ with this branch
and let me know what you think.
I was curious how to reproduce/ understand the changes in appview.py, reviews.py that cause the model to
be "None". I revert that change but for me its not crashing so I wonder if that is still needed? Or is it
a random issue? Except for this (which I just would like to understand better how it happens) the branch
is +10 approve ;)
Additional thanks for unifying get_mock_options() and the with open() fix (and the cleanup) in test_database.py.