Merge lp:~mvo/software-center/test-catview-cleanup into lp:software-center
- test-catview-cleanup
- Merge into trunk
Proposed by
Michael Vogt
Status: | Superseded |
---|---|
Proposed branch: | lp:~mvo/software-center/test-catview-cleanup |
Merge into: | lp:software-center |
Diff against target: |
879 lines (+350/-246) 7 files modified
softwarecenter/backend/channel.py (+4/-4) softwarecenter/db/application.py (+5/-4) softwarecenter/testutils.py (+62/-0) softwarecenter/ui/gtk3/models/appstore2.py (+11/-5) softwarecenter/ui/gtk3/views/catview_gtk.py (+48/-21) test/gtk3/test_app.py (+2/-36) test/gtk3/test_catview.py (+218/-176) |
To merge this branch: | bzr merge lp:~mvo/software-center/test-catview-cleanup |
Related bugs: |
Reviewer | Review Type | Date Requested | Status |
---|---|---|---|
software-store-developers | Pending | ||
Review via email: mp+106766@code.launchpad.net |
This proposal has been superseded by a proposal from 2012-05-22.
Commit message
Description of the change
This branch refactors the test_catview.py code:
- split the TestCases for the Recommender and the Top Rated/Whats New
- remove duplicated code in the tests by using setUp()/tearDown() more efficiently
- use the patch decorator more
- avoid re-creating the DB in each test via setUpClass, this could be optimized further by just having one global instance for the entire test
To post a comment you must log in.
- 3034. By Michael Vogt
-
test/gtk3/
test_catview. py: use addCleanup() instead of tearDown() as suggested by Natalia - 3035. By Michael Vogt
-
merge 5.2 and resolve conflicts
Unmerged revisions
Preview Diff
[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
1 | === modified file 'softwarecenter/backend/channel.py' |
2 | --- softwarecenter/backend/channel.py 2012-03-19 14:20:55 +0000 |
3 | +++ softwarecenter/backend/channel.py 2012-05-22 12:42:24 +0000 |
4 | @@ -22,7 +22,7 @@ |
5 | |
6 | from gettext import gettext as _ |
7 | |
8 | -from softwarecenter.distro import get_distro |
9 | +import softwarecenter.distro |
10 | |
11 | from softwarecenter.enums import (SortMethods, |
12 | Icons, |
13 | @@ -34,7 +34,7 @@ |
14 | |
15 | class ChannelsManager(object): |
16 | def __init__(self, db, **kwargs): |
17 | - self.distro = get_distro() |
18 | + self.distro = softwarecenter.distro.get_distro() |
19 | self.db = db |
20 | |
21 | # public |
22 | @@ -154,7 +154,7 @@ |
23 | self.installed_only = installed_only |
24 | self._channel_sort_mode = channel_sort_mode |
25 | # distro specific stuff |
26 | - self.distro = get_distro() |
27 | + self.distro = softwarecenter.distro.get_distro() |
28 | # configure the channel |
29 | self._channel_display_name = self._get_display_name_for_channel( |
30 | channel_name, channel_origin, channel_component) |
31 | @@ -351,7 +351,7 @@ |
32 | return AptChannelsManager.channel_available(channelname) |
33 | |
34 | if __name__ == "__main__": |
35 | - distro = get_distro() |
36 | + distro = softwarecenter.distro.get_distro() |
37 | channel = SoftwareChannel(distro.get_distro_channel_name(), |
38 | None, None) |
39 | print(channel) |
40 | |
41 | === modified file 'softwarecenter/db/application.py' |
42 | --- softwarecenter/db/application.py 2012-05-11 13:48:04 +0000 |
43 | +++ softwarecenter/db/application.py 2012-05-22 12:42:24 +0000 |
44 | @@ -24,9 +24,10 @@ |
45 | import os |
46 | import re |
47 | |
48 | +import softwarecenter.distro |
49 | + |
50 | from gettext import gettext as _ |
51 | from softwarecenter.backend.channel import is_channel_available |
52 | -from softwarecenter.distro import get_distro |
53 | from softwarecenter.enums import PkgStates, XapianValues, Icons |
54 | |
55 | from softwarecenter.paths import (APP_INSTALL_CHANNELS_PATH, |
56 | @@ -177,7 +178,7 @@ |
57 | self._db = db |
58 | self._db.connect("reopen", self._on_db_reopen) |
59 | self._cache = self._db._aptcache |
60 | - self._distro = get_distro() |
61 | + self._distro = softwarecenter.distro.get_distro() |
62 | self._history = None |
63 | # import here (intead of global) to avoid dbus dependency |
64 | # in update-software-center (that imports application, but |
65 | @@ -298,7 +299,7 @@ |
66 | # try apt first |
67 | if self._pkg: |
68 | for origin in self._pkg.candidate.origins: |
69 | - if (origin.origin == get_distro().get_distro_channel_name() and |
70 | + if (origin.origin == self._distro.get_distro_channel_name() and |
71 | origin.trusted and origin.component): |
72 | return origin.component |
73 | # then xapian |
74 | @@ -639,7 +640,7 @@ |
75 | self.emit("screenshots-available", self._screenshot_list) |
76 | return |
77 | # download it |
78 | - distro = get_distro() |
79 | + distro = self._distro |
80 | url = distro.SCREENSHOT_JSON_URL % self._app.pkgname |
81 | try: |
82 | f = Gio.File.new_for_uri(url) |
83 | |
84 | === modified file 'softwarecenter/testutils.py' |
85 | --- softwarecenter/testutils.py 2012-04-16 16:41:29 +0000 |
86 | +++ softwarecenter/testutils.py 2012-05-22 12:42:24 +0000 |
87 | @@ -22,6 +22,8 @@ |
88 | import tempfile |
89 | import time |
90 | |
91 | +from collections import defaultdict |
92 | + |
93 | from mock import Mock |
94 | |
95 | m_dbus = m_polkit = m_aptd = None |
96 | @@ -142,6 +144,12 @@ |
97 | main_loop.iteration() |
98 | |
99 | |
100 | +def do_events_with_sleep(iterations=5, sleep=0.1): |
101 | + for i in range(iterations): |
102 | + do_events() |
103 | + time.sleep(sleep) |
104 | + |
105 | + |
106 | def get_mock_app_from_real_app(real_app): |
107 | """ take a application and return a app where the details are a mock |
108 | of the real details so they can easily be modified |
109 | @@ -314,3 +322,57 @@ |
110 | {u'rating': 1.5, u'package_name': u'tucan'}], |
111 | u'app': u'pitivi'} |
112 | return recommend_app_data |
113 | + |
114 | + |
115 | +class ObjectWithSignals(object): |
116 | + """A faked object that you can connect to and emit signals.""" |
117 | + |
118 | + def __init__(self, *a, **kw): |
119 | + super(ObjectWithSignals, self).__init__() |
120 | + self._callbacks = defaultdict(list) |
121 | + |
122 | + def connect(self, signal, callback): |
123 | + """Connect a signal with a callback.""" |
124 | + self._callbacks[signal].append(callback) |
125 | + |
126 | + def disconnect(self, signal, callback): |
127 | + """Connect a signal with a callback.""" |
128 | + self._callbacks[signal].remove(callback) |
129 | + if len(self._callbacks[signal]) == 0: |
130 | + self._callbacks.pop(signal) |
131 | + |
132 | + def disconnect_by_func(self, callback): |
133 | + """Disconnect 'callback' from every signal.""" |
134 | + # do not use iteritems since we may change the dict inside the for |
135 | + for signal, callbacks in self._callbacks.items(): |
136 | + if callback in callbacks: |
137 | + self.disconnect(signal, callback) |
138 | + |
139 | + def emit(self, signal, *args, **kwargs): |
140 | + """Emit 'signal' passing *args, **kwargs to every callback.""" |
141 | + for callback in self._callbacks[signal]: |
142 | + callback(*args, **kwargs) |
143 | + |
144 | + |
145 | +class FakedCache(ObjectWithSignals, dict): |
146 | + """A faked cache.""" |
147 | + |
148 | + def __init__(self, *a, **kw): |
149 | + super(FakedCache, self).__init__() |
150 | + self.ready = False |
151 | + |
152 | + def open(self): |
153 | + """Open this cache.""" |
154 | + self.ready = True |
155 | + |
156 | + def component_available(self, distro_codename, component): |
157 | + """Return whether 'component' is available in 'distro_codename'.""" |
158 | + |
159 | + def get_addons(self, pkgname): |
160 | + """Return (recommended, suggested) addons for 'pkgname'.""" |
161 | + return ([], []) |
162 | + |
163 | + def get_total_size_on_install(self, pkgname, addons_to_install, |
164 | + addons_to_remove, archive_suite): |
165 | + """Return a fake (total_download_size, total_install_size) result.""" |
166 | + return (0, 0) |
167 | |
168 | === modified file 'softwarecenter/ui/gtk3/models/appstore2.py' |
169 | --- softwarecenter/ui/gtk3/models/appstore2.py 2012-05-18 18:42:27 +0000 |
170 | +++ softwarecenter/ui/gtk3/models/appstore2.py 2012-05-22 12:42:24 +0000 |
171 | @@ -115,9 +115,7 @@ |
172 | self.icons = icons |
173 | self.icon_size = icon_size |
174 | |
175 | - # cache the 'missing icon' used in the treeview for apps without an |
176 | - # icon |
177 | - self._missing_icon = icons.load_icon(Icons.MISSING_APP, icon_size, 0) |
178 | + self._missing_icon = None # delay this until actually needed |
179 | if global_icon_cache: |
180 | self.icon_cache = _app_icon_cache |
181 | else: |
182 | @@ -146,6 +144,14 @@ |
183 | on_image_download_complete, pkgname) |
184 | image_downloader.download_file(url, icon_file_path) |
185 | |
186 | + @property |
187 | + def missing_icon(self): |
188 | + # cache the 'missing icon' used in treeviews for apps without an icon |
189 | + if self._missing_icon is None: |
190 | + self._missing_icon = self.icons.load_icon(Icons.MISSING_APP, |
191 | + self.icon_size, 0) |
192 | + return self._missing_icon |
193 | + |
194 | def update_availability(self, doc): |
195 | doc.available = None |
196 | doc.installed = None |
197 | @@ -228,10 +234,10 @@ |
198 | self.get_pkgname(doc), |
199 | full_icon_file_name) |
200 | # display the missing icon while the real one downloads |
201 | - self.icon_cache[icon_name] = self._missing_icon |
202 | + self.icon_cache[icon_name] = self.missing_icon |
203 | except GObject.GError as e: |
204 | LOG.debug("get_icon returned '%s'" % e) |
205 | - return self._missing_icon |
206 | + return self.missing_icon |
207 | |
208 | def get_review_stats(self, doc): |
209 | return self.review_loader.get_review_stats(self.get_application(doc)) |
210 | |
211 | === modified file 'softwarecenter/ui/gtk3/views/catview_gtk.py' |
212 | --- softwarecenter/ui/gtk3/views/catview_gtk.py 2012-03-16 16:52:54 +0000 |
213 | +++ softwarecenter/ui/gtk3/views/catview_gtk.py 2012-05-22 12:42:24 +0000 |
214 | @@ -28,9 +28,12 @@ |
215 | |
216 | import softwarecenter.paths |
217 | from softwarecenter.db.application import Application |
218 | -from softwarecenter.enums import (NonAppVisibility, |
219 | - SortMethods, |
220 | - TOP_RATED_CAROUSEL_LIMIT) |
221 | +from softwarecenter.enums import ( |
222 | + NonAppVisibility, |
223 | + PkgStates, |
224 | + SortMethods, |
225 | + TOP_RATED_CAROUSEL_LIMIT, |
226 | +) |
227 | from softwarecenter.utils import wait_for_apt_cache_ready |
228 | from softwarecenter.ui.gtk3.models.appstore2 import AppPropertiesHelper |
229 | from softwarecenter.ui.gtk3.widgets.viewport import Viewport |
230 | @@ -224,6 +227,8 @@ |
231 | apps_filter, apps_limit=0): |
232 | CategoriesViewGtk.__init__(self, datadir, desktopdir, cache, db, icons, |
233 | apps_filter, apps_limit=0) |
234 | + self.top_rated = None |
235 | + self.exhibit_banner = None |
236 | |
237 | # sections |
238 | self.departments = None |
239 | @@ -337,24 +342,41 @@ |
240 | flags=['nonapps-visible']) |
241 | self.emit("category-selected", cat) |
242 | |
243 | + def _pkg_available(self, pkgname): |
244 | + try: |
245 | + state = Application("", pkgname).get_details(self.db).pkg_state |
246 | + except: |
247 | + available = False |
248 | + else: |
249 | + available = state not in (PkgStates.NOT_FOUND, PkgStates.ERROR) |
250 | + return available |
251 | + |
252 | + def _filter_and_set_exhibits(self, sca_client, exhibit_list): |
253 | + result = [] |
254 | + # filter out those exhibits that are not available in this run |
255 | + for exhibit in exhibit_list: |
256 | + available = all(self._pkg_available(p) for p in |
257 | + exhibit.package_names.split(',')) |
258 | + if available: |
259 | + result.append(exhibit) |
260 | + |
261 | + self.exhibit_banner.set_exhibits(result) |
262 | + |
263 | def _append_banner_ads(self): |
264 | - exhibit_banner = ExhibitBanner() |
265 | - exhibit_banner.set_exhibits([FeaturedExhibit(), |
266 | - ]) |
267 | - exhibit_banner.connect("show-exhibits-clicked", self._on_show_exhibits) |
268 | + self.exhibit_banner = ExhibitBanner() |
269 | + self.exhibit_banner.set_exhibits([FeaturedExhibit()]) |
270 | + self.exhibit_banner.connect( |
271 | + "show-exhibits-clicked", self._on_show_exhibits) |
272 | |
273 | # query using the agent |
274 | scagent = SoftwareCenterAgent() |
275 | - scagent.connect( |
276 | - "exhibits", lambda sca, l: exhibit_banner.set_exhibits(l)) |
277 | + scagent.connect("exhibits", self._filter_and_set_exhibits) |
278 | scagent.query_exhibits() |
279 | |
280 | a = Gtk.Alignment() |
281 | a.set_padding(0, StockEms.SMALL, 0, 0) |
282 | - a.add(exhibit_banner) |
283 | - |
284 | + a.add(self.exhibit_banner) |
285 | self.vbox.pack_start(a, False, False, 0) |
286 | - return |
287 | |
288 | def _append_departments(self): |
289 | # set the departments section to use the label markup we have just |
290 | @@ -382,6 +404,8 @@ |
291 | # FIXME: _update_{top_rated,whats_new,recommended_for_you}_content() |
292 | # duplicates a lot of code |
293 | def _update_top_rated_content(self): |
294 | + if self.top_rated is None: |
295 | + return |
296 | # remove any existing children from the grid widget |
297 | self.top_rated.remove_all() |
298 | # get top_rated category and docs |
299 | @@ -715,21 +739,24 @@ |
300 | #return |
301 | |
302 | |
303 | -def get_test_window_catview(): |
304 | +def get_test_window_catview(db=None): |
305 | |
306 | def on_category_selected(view, cat): |
307 | print "on_category_selected view: ", view |
308 | print "on_category_selected cat: ", cat |
309 | |
310 | - from softwarecenter.db.pkginfo import get_pkg_info |
311 | - cache = get_pkg_info() |
312 | - cache.open() |
313 | + if db is None: |
314 | + from softwarecenter.db.pkginfo import get_pkg_info |
315 | + cache = get_pkg_info() |
316 | + cache.open() |
317 | |
318 | - from softwarecenter.db.database import StoreDatabase |
319 | - xapian_base_path = "/var/cache/software-center" |
320 | - pathname = os.path.join(xapian_base_path, "xapian") |
321 | - db = StoreDatabase(pathname, cache) |
322 | - db.open() |
323 | + from softwarecenter.db.database import StoreDatabase |
324 | + xapian_base_path = "/var/cache/software-center" |
325 | + pathname = os.path.join(xapian_base_path, "xapian") |
326 | + db = StoreDatabase(pathname, cache) |
327 | + db.open() |
328 | + else: |
329 | + cache = db._aptcache |
330 | |
331 | import softwarecenter.paths |
332 | datadir = softwarecenter.paths.datadir |
333 | |
334 | === modified file 'test/gtk3/test_app.py' |
335 | --- test/gtk3/test_app.py 2012-05-16 15:52:07 +0000 |
336 | +++ test/gtk3/test_app.py 2012-05-22 12:42:24 +0000 |
337 | @@ -8,7 +8,7 @@ |
338 | |
339 | from mock import Mock |
340 | |
341 | -from testutils import get_mock_options, setup_test_env |
342 | +from testutils import FakedCache, get_mock_options, setup_test_env |
343 | setup_test_env() |
344 | |
345 | import softwarecenter.paths |
346 | @@ -17,40 +17,6 @@ |
347 | from softwarecenter.ui.gtk3 import app |
348 | |
349 | |
350 | -class FakedCache(dict): |
351 | - """A faked cache.""" |
352 | - |
353 | - def __init__(self, *a, **kw): |
354 | - super(FakedCache, self).__init__() |
355 | - self._callbacks = defaultdict(list) |
356 | - self.ready = False |
357 | - |
358 | - def open(self): |
359 | - """Open this cache.""" |
360 | - self.ready = True |
361 | - |
362 | - def connect(self, signal, callback): |
363 | - """Connect a signal with a callback.""" |
364 | - self._callbacks[signal].append(callback) |
365 | - |
366 | - def disconnect_by_func(self, callback): |
367 | - """Disconnect 'callback' from every signal.""" |
368 | - for signal, cb in self._callbacks.iteritems(): |
369 | - if cb == callback: |
370 | - self._callbacks[signal].remove(callback) |
371 | - if len(self._callbacks[signal]) == 0: |
372 | - self._callbacks.pop(signal) |
373 | - |
374 | - def get_addons(self, pkgname): |
375 | - """Return (recommended, suggested) addons for 'pkgname'.""" |
376 | - return ([],[]) |
377 | - |
378 | - def get_total_size_on_install(self,pkgname, addons_to_install, |
379 | - addons_to_remove, archive_suite): |
380 | - """Return a fake (total_download_size, total_install_size) result.""" |
381 | - return (0, 0) |
382 | - |
383 | - |
384 | class ParsePackagesArgsTestCase(unittest.TestCase): |
385 | """Test suite for the parse_packages_args helper.""" |
386 | |
387 | @@ -109,7 +75,7 @@ |
388 | fname = __file__ |
389 | assert os.path.exists(fname) |
390 | self.assertRaises(DebFileOpenError, app.parse_packages_args, fname) |
391 | - |
392 | + |
393 | |
394 | class ParsePackagesWithAptPrefixTestCase(ParsePackagesArgsTestCase): |
395 | |
396 | |
397 | === modified file 'test/gtk3/test_catview.py' |
398 | --- test/gtk3/test_catview.py 2012-03-15 22:23:21 +0000 |
399 | +++ test/gtk3/test_catview.py 2012-05-22 12:42:24 +0000 |
400 | @@ -1,66 +1,81 @@ |
401 | +import unittest |
402 | + |
403 | from gi.repository import Gtk |
404 | -import time |
405 | -import unittest |
406 | from mock import patch, Mock |
407 | |
408 | from testutils import setup_test_env |
409 | setup_test_env() |
410 | |
411 | +import softwarecenter.distro |
412 | +import softwarecenter.paths |
413 | + |
414 | +from softwarecenter.db.database import StoreDatabase |
415 | from softwarecenter.enums import SortMethods |
416 | -from softwarecenter.testutils import (get_test_db, |
417 | - make_recommender_agent_recommend_me_dict) |
418 | - |
419 | -class TestCatView(unittest.TestCase): |
420 | +from softwarecenter.testutils import ( |
421 | + do_events_with_sleep, |
422 | + do_events, |
423 | + FakedCache, |
424 | + get_test_db, |
425 | + make_recommender_agent_recommend_me_dict, |
426 | + ObjectWithSignals, |
427 | +) |
428 | +from softwarecenter.ui.gtk3.views import catview_gtk |
429 | +from softwarecenter.ui.gtk3.views.catview_gtk import get_test_window_catview |
430 | +from softwarecenter.ui.gtk3.widgets.containers import FramedHeaderBox |
431 | +from softwarecenter.ui.gtk3.widgets.spinner import SpinnerNotebook |
432 | + |
433 | + |
434 | +class CatViewBaseTestCase(unittest.TestCase): |
435 | + |
436 | + @classmethod |
437 | + def setUpClass(cls): |
438 | + cls.db = get_test_db() |
439 | |
440 | def setUp(self): |
441 | - self.db = get_test_db() |
442 | + self._cat = None |
443 | + self.win = get_test_window_catview(self.db) |
444 | + self.addCleanup(self.win.destroy) |
445 | + self.notebook = self.win.get_child() |
446 | + self.lobby = self.win.get_data("lobby") |
447 | + self.subcat_view = self.win.get_data("subcat") |
448 | + self.rec_panel = self.lobby.recommended_for_you_panel |
449 | |
450 | def _on_category_selected(self, subcatview, category): |
451 | - #print "**************", subcatview, category |
452 | self._cat = category |
453 | - |
454 | - def test_subcatview_top_rated(self): |
455 | - from softwarecenter.ui.gtk3.views.catview_gtk import get_test_window_catview |
456 | - # get the widgets we need |
457 | - win = get_test_window_catview() |
458 | - lobby = win.get_data("lobby") |
459 | - |
460 | + |
461 | + |
462 | +class TopAndWhatsNewTestCase(CatViewBaseTestCase): |
463 | + |
464 | + def test_top_rated(self): |
465 | # simulate review-stats refresh |
466 | - lobby._update_top_rated_content = Mock() |
467 | - lobby.reviews_loader.emit("refresh-review-stats-finished", []) |
468 | - self.assertTrue(lobby._update_top_rated_content.called) |
469 | + self.lobby._update_top_rated_content = Mock() |
470 | + self.lobby.reviews_loader.emit("refresh-review-stats-finished", []) |
471 | + self.assertTrue(self.lobby._update_top_rated_content.called) |
472 | |
473 | # test clicking top_rated |
474 | - lobby.connect("category-selected", self._on_category_selected) |
475 | - lobby.top_rated_frame.more.clicked() |
476 | - self._p() |
477 | + self.lobby.connect("category-selected", self._on_category_selected) |
478 | + self.lobby.top_rated_frame.more.clicked() |
479 | + do_events() |
480 | self.assertNotEqual(self._cat, None) |
481 | self.assertEqual(self._cat.name, "Top Rated") |
482 | self.assertEqual(self._cat.sortmode, SortMethods.BY_TOP_RATED) |
483 | - win.destroy() |
484 | - |
485 | - def test_subcatview_new(self): |
486 | - from softwarecenter.ui.gtk3.views.catview_gtk import get_test_window_catview |
487 | - # get the widgets we need |
488 | - win = get_test_window_catview() |
489 | - lobby = win.get_data("lobby") |
490 | - |
491 | + |
492 | + def test_new(self): |
493 | # test db reopen triggers whats-new update |
494 | - lobby._update_whats_new_content = Mock() |
495 | - lobby.db.emit("reopen") |
496 | - self.assertTrue(lobby._update_whats_new_content.called) |
497 | + self.lobby._update_whats_new_content = Mock() |
498 | + self.lobby.db.emit("reopen") |
499 | + self.assertTrue(self.lobby._update_whats_new_content.called) |
500 | |
501 | # test clicking new |
502 | - lobby.connect("category-selected", self._on_category_selected) |
503 | - lobby.whats_new_frame.more.clicked() |
504 | - self._p() |
505 | + self.lobby.connect("category-selected", self._on_category_selected) |
506 | + self.lobby.whats_new_frame.more.clicked() |
507 | + do_events() |
508 | self.assertNotEqual(self._cat, None) |
509 | # encoding is utf-8 (since r2218, see category.py) |
510 | self.assertEqual(self._cat.name, 'What\xe2\x80\x99s New') |
511 | self.assertEqual(self._cat.sortmode, SortMethods.BY_CATALOGED_TIME) |
512 | - win.destroy() |
513 | |
514 | - def test_subcatview_new_no_sort_info_yet(self): |
515 | + def test_new_no_sort_info_yet(self): |
516 | # ensure that we don't show a empty "whats new" category |
517 | # see LP: #865985 |
518 | from softwarecenter.testutils import get_test_db |
519 | @@ -68,7 +83,7 @@ |
520 | cache = db._aptcache |
521 | # simulate a fresh install with no catalogedtime info |
522 | del db._axi_values["catalogedtime"] |
523 | - |
524 | + |
525 | from softwarecenter.testutils import get_test_gtk3_icon_cache |
526 | icons = get_test_gtk3_icon_cache() |
527 | |
528 | @@ -76,7 +91,6 @@ |
529 | apps_filter = AppFilter(db, cache) |
530 | |
531 | from softwarecenter.distro import get_distro |
532 | - import softwarecenter.paths |
533 | from softwarecenter.paths import APP_INSTALL_PATH |
534 | from softwarecenter.ui.gtk3.views.catview_gtk import LobbyViewGtk |
535 | view = LobbyViewGtk(softwarecenter.paths.datadir, APP_INSTALL_PATH, |
536 | @@ -85,6 +99,7 @@ |
537 | |
538 | # gui |
539 | win = Gtk.Window() |
540 | + self.addCleanup(win.destroy) |
541 | win.set_size_request(800, 400) |
542 | |
543 | scroll = Gtk.ScrolledWindow() |
544 | @@ -93,171 +108,198 @@ |
545 | win.add(scroll) |
546 | win.show() |
547 | # test visibility |
548 | - self._p() |
549 | + do_events() |
550 | self.assertFalse(view.whats_new_frame.get_property("visible")) |
551 | - self._p() |
552 | - win.destroy() |
553 | - |
554 | - def test_subcatview_recommended_for_you_opt_in_display(self): |
555 | - |
556 | - # patch the recommender UUID value to insure that we are not opted-in for this test |
557 | - get_recommender_opted_in_patcher = patch('softwarecenter.backend.recagent.RecommenderAgent.is_opted_in') |
558 | - self.addCleanup(get_recommender_opted_in_patcher.stop) |
559 | - mock_get_recommender_opted_in = get_recommender_opted_in_patcher.start() |
560 | - mock_get_recommender_opted_in.return_value = False |
561 | - |
562 | - from softwarecenter.ui.gtk3.views.catview_gtk import get_test_window_catview |
563 | - # get the widgets we need |
564 | - win = get_test_window_catview() |
565 | - lobby = win.get_data("lobby") |
566 | - rec_panel = lobby.recommended_for_you_panel |
567 | - self._p() |
568 | - from softwarecenter.ui.gtk3.widgets.containers import FramedHeaderBox |
569 | - self.assertTrue(rec_panel.spinner_notebook.get_current_page() == FramedHeaderBox.CONTENT) |
570 | - self.assertTrue(rec_panel.opt_in_vbox.get_property("visible")) |
571 | - win.destroy() |
572 | - |
573 | - # patch out the agent query method to avoid making the actual server call |
574 | - @patch('softwarecenter.backend.recagent.RecommenderAgent' |
575 | - '.post_submit_profile') |
576 | - def test_subcatview_recommended_for_you_spinner_display(self, mock_query): |
577 | - |
578 | - # patch the recommender UUID value to insure that we are not opted-in for this test |
579 | - get_recommender_opted_in_patcher = patch('softwarecenter.backend.recagent.RecommenderAgent.is_opted_in') |
580 | - self.addCleanup(get_recommender_opted_in_patcher.stop) |
581 | - mock_get_recommender_opted_in = get_recommender_opted_in_patcher.start() |
582 | - mock_get_recommender_opted_in.return_value = False |
583 | - |
584 | - from softwarecenter.ui.gtk3.views.catview_gtk import get_test_window_catview |
585 | - # get the widgets we need |
586 | - win = get_test_window_catview() |
587 | - lobby = win.get_data("lobby") |
588 | - rec_panel = lobby.recommended_for_you_panel |
589 | - self._p() |
590 | - # click the opt-in button to initiate the process, this will show the spinner |
591 | - rec_panel.opt_in_button.emit('clicked') |
592 | - self._p() |
593 | - from softwarecenter.ui.gtk3.widgets.spinner import SpinnerNotebook |
594 | - self.assertTrue(rec_panel.spinner_notebook.get_current_page() == SpinnerNotebook.SPINNER_PAGE) |
595 | - self.assertTrue(rec_panel.opt_in_vbox.get_property("visible")) |
596 | - win.destroy() |
597 | - |
598 | - # patch out the agent query method to avoid making the actual server call |
599 | - @patch('softwarecenter.backend.recagent.RecommenderAgent' |
600 | - '.post_submit_profile') |
601 | - def test_subcatview_recommended_for_you_display_recommendations(self, mock_query): |
602 | - |
603 | - # patch the recommender UUID value to insure that we are not opted-in for this test |
604 | - get_recommender_opted_in_patcher = patch('softwarecenter.backend.recagent.RecommenderAgent.is_opted_in') |
605 | - self.addCleanup(get_recommender_opted_in_patcher.stop) |
606 | - mock_get_recommender_opted_in = get_recommender_opted_in_patcher.start() |
607 | - mock_get_recommender_opted_in.return_value = False |
608 | - |
609 | - from softwarecenter.ui.gtk3.views.catview_gtk import get_test_window_catview |
610 | - # get the widgets we need |
611 | - win = get_test_window_catview() |
612 | - lobby = win.get_data("lobby") |
613 | - rec_panel = lobby.recommended_for_you_panel |
614 | - self._p() |
615 | - # click the opt-in button to initiate the process, this will show the spinner |
616 | - rec_panel.opt_in_button.emit('clicked') |
617 | - self._p() |
618 | - rec_panel._update_recommended_for_you_content() |
619 | - self._p() |
620 | + |
621 | + |
622 | +class RecommendationsTestCase(CatViewBaseTestCase): |
623 | + """The test suite for the recommendations .""" |
624 | + |
625 | + @patch('softwarecenter.backend.recagent.RecommenderAgent.is_opted_in') |
626 | + def test_recommended_for_you_opt_in_display( |
627 | + self, mock_get_recommender_opted_in): |
628 | + # patch the recommender UUID value to ensure that we are not opted-in |
629 | + # for this test |
630 | + mock_get_recommender_opted_in.return_value = False |
631 | + |
632 | + do_events() |
633 | + self.assertEqual(self.rec_panel.spinner_notebook.get_current_page(), |
634 | + FramedHeaderBox.CONTENT) |
635 | + self.assertTrue(self.rec_panel.opt_in_vbox.get_property("visible")) |
636 | + |
637 | + # patch out the agent query method to avoid making the actual server call |
638 | + @patch('softwarecenter.backend.recagent.RecommenderAgent.is_opted_in') |
639 | + @patch('softwarecenter.backend.recagent.RecommenderAgent' |
640 | + '.post_submit_profile') |
641 | + def test_recommended_for_you_spinner_display( |
642 | + self, mock_query, mock_get_recommender_opted_in): |
643 | + # patch the recommender UUID value to insure that we are not opted-in |
644 | + # for this test |
645 | + mock_get_recommender_opted_in.return_value = False |
646 | + |
647 | + # click the opt-in button to initiate the process, |
648 | + # this will show the spinner |
649 | + self.rec_panel.opt_in_button.emit('clicked') |
650 | + do_events() |
651 | + self.assertEqual(self.rec_panel.spinner_notebook.get_current_page(), |
652 | + SpinnerNotebook.SPINNER_PAGE) |
653 | + self.assertTrue(self.rec_panel.opt_in_vbox.get_property("visible")) |
654 | + |
655 | + # patch out the agent query method to avoid making the actual server call |
656 | + @patch('softwarecenter.backend.recagent.RecommenderAgent.is_opted_in') |
657 | + @patch('softwarecenter.backend.recagent.RecommenderAgent' |
658 | + '.post_submit_profile') |
659 | + def test_recommended_for_you_display_recommendations(self, |
660 | + mock_query, mock_get_recommender_opted_in): |
661 | + # patch the recommender UUID value to insure that we are not opted-in |
662 | + # for this test |
663 | + mock_get_recommender_opted_in.return_value = False |
664 | + |
665 | + # click the opt-in button to initiate the process, |
666 | + # this will show the spinner |
667 | + self.rec_panel.opt_in_button.emit('clicked') |
668 | + do_events() |
669 | + self.rec_panel._update_recommended_for_you_content() |
670 | + do_events() |
671 | # we fake the callback from the agent here |
672 | - lobby.recommended_for_you_panel.recommended_for_you_cat._recommend_me_result( |
673 | - None, |
674 | - make_recommender_agent_recommend_me_dict()) |
675 | - self.assertNotEqual( |
676 | - lobby.recommended_for_you_panel.recommended_for_you_cat.get_documents(self.db), []) |
677 | - from softwarecenter.ui.gtk3.widgets.spinner import SpinnerNotebook |
678 | - self.assertTrue(rec_panel.spinner_notebook.get_current_page() == SpinnerNotebook.CONTENT_PAGE) |
679 | - self._p() |
680 | + for_you = self.lobby.recommended_for_you_panel.recommended_for_you_cat |
681 | + for_you._recommend_me_result(None, |
682 | + make_recommender_agent_recommend_me_dict()) |
683 | + self.assertNotEqual(for_you.get_documents(self.db), []) |
684 | + self.assertEqual(self.rec_panel.spinner_notebook.get_current_page(), |
685 | + SpinnerNotebook.CONTENT_PAGE) |
686 | + do_events() |
687 | # test clicking recommended_for_you More button |
688 | - lobby.connect("category-selected", self._on_category_selected) |
689 | - lobby.recommended_for_you_panel.more.clicked() |
690 | - self._p() |
691 | + self.lobby.connect("category-selected", self._on_category_selected) |
692 | + self.lobby.recommended_for_you_panel.more.clicked() |
693 | + # this is delayed for some reason so we need to sleep here |
694 | + do_events_with_sleep() |
695 | self.assertNotEqual(self._cat, None) |
696 | self.assertEqual(self._cat.name, "Recommended For You") |
697 | - win.destroy() |
698 | - |
699 | + |
700 | # patch out the agent query method to avoid making the actual server call |
701 | + @patch('softwarecenter.backend.recagent.RecommenderAgent.is_opted_in') |
702 | @patch('softwarecenter.backend.recagent.RecommenderAgent' |
703 | '.query_recommend_me') |
704 | - def test_subcatview_recommended_for_you_display_recommendations_not_opted_in(self, mock_query): |
705 | - |
706 | - # patch the recommender UUID value to insure that we are not opted-in for this test |
707 | - get_recommender_opted_in_patcher = patch('softwarecenter.backend.recagent.RecommenderAgent.is_opted_in') |
708 | - self.addCleanup(get_recommender_opted_in_patcher.stop) |
709 | - mock_get_recommender_opted_in = get_recommender_opted_in_patcher.start() |
710 | + def test_recommended_for_you_display_recommendations_not_opted_in(self, |
711 | + mock_query, mock_get_recommender_opted_in): |
712 | + # patch the recommender UUID value to insure that we are not opted-in |
713 | + # for this test |
714 | mock_get_recommender_opted_in.return_value = False |
715 | - |
716 | - from softwarecenter.ui.gtk3.views.catview_gtk import get_test_window_catview |
717 | - # get the widgets we need |
718 | - win = get_test_window_catview() |
719 | + |
720 | # we want to work in the "subcat" view |
721 | - notebook = win.get_child() |
722 | - notebook.next_page() |
723 | - |
724 | - subcat_view = win.get_data("subcat") |
725 | - self._p() |
726 | - self.assertFalse(subcat_view.recommended_for_you_in_cat.get_property("visible")) |
727 | - win.destroy() |
728 | - |
729 | + self.notebook.next_page() |
730 | + |
731 | + do_events() |
732 | + visible = self.subcat_view.recommended_for_you_in_cat.get_property( |
733 | + "visible") |
734 | + self.assertFalse(visible) |
735 | + |
736 | # patch out the agent query method to avoid making the actual server call |
737 | + @patch('softwarecenter.backend.recagent.RecommenderAgent.is_opted_in') |
738 | @patch('softwarecenter.backend.recagent.RecommenderAgent' |
739 | '.query_recommend_me') |
740 | - def test_subcatview_recommended_for_you_display_recommendations_opted_in(self, mock_query): |
741 | - |
742 | - # patch the recommender UUID value to insure that we are not opted-in for this test |
743 | - get_recommender_opted_in_patcher = patch('softwarecenter.backend.recagent.RecommenderAgent.is_opted_in') |
744 | - self.addCleanup(get_recommender_opted_in_patcher.stop) |
745 | - mock_get_recommender_opted_in = get_recommender_opted_in_patcher.start() |
746 | + def test_recommended_for_you_display_recommendations_opted_in( |
747 | + self, mock_query, mock_get_recommender_opted_in): |
748 | + # patch the recommender UUID value to insure that we are not opted-in |
749 | + # for this test |
750 | mock_get_recommender_opted_in.return_value = True |
751 | - |
752 | - from softwarecenter.ui.gtk3.views.catview_gtk import get_test_window_catview |
753 | - # get the widgets we need |
754 | - win = get_test_window_catview() |
755 | + |
756 | # we want to work in the "subcat" view |
757 | - notebook = win.get_child() |
758 | - notebook.next_page() |
759 | - |
760 | - subcat_view = win.get_data("subcat") |
761 | - rec_cat_panel = subcat_view.recommended_for_you_in_cat |
762 | - self._p() |
763 | + self.notebook.next_page() |
764 | + |
765 | + rec_cat_panel = self.subcat_view.recommended_for_you_in_cat |
766 | rec_cat_panel._update_recommended_for_you_content() |
767 | - self._p() |
768 | + do_events() |
769 | # we fake the callback from the agent here |
770 | rec_cat_panel.recommended_for_you_cat._recommend_me_result( |
771 | None, |
772 | make_recommender_agent_recommend_me_dict()) |
773 | - result_docs = rec_cat_panel.recommended_for_you_cat.get_documents(self.db) |
774 | + result_docs = rec_cat_panel.recommended_for_you_cat.get_documents( |
775 | + self.db) |
776 | self.assertNotEqual(result_docs, []) |
777 | - # check that we are getting the correct number of results, corresponding |
778 | - # to the following Internet items: |
779 | + # check that we are getting the correct number of results, |
780 | + # corresponding to the following Internet items: |
781 | # Mangler, Midori, Midori Private Browsing, Psi |
782 | self.assertTrue(len(result_docs) == 4) |
783 | - from softwarecenter.ui.gtk3.widgets.spinner import SpinnerNotebook |
784 | - self.assertTrue(rec_cat_panel.spinner_notebook.get_current_page() == SpinnerNotebook.CONTENT_PAGE) |
785 | + self.assertEqual(rec_cat_panel.spinner_notebook.get_current_page(), |
786 | + SpinnerNotebook.CONTENT_PAGE) |
787 | # check that the tiles themselves are visible |
788 | - self._p() |
789 | - self.assertTrue(rec_cat_panel.recommended_for_you_content.get_property("visible")) |
790 | - self.assertTrue(rec_cat_panel.recommended_for_you_content.get_children()[0].title.get_property("visible")) |
791 | - self._p() |
792 | + do_events() |
793 | + self.assertTrue(rec_cat_panel.recommended_for_you_content.get_property( |
794 | + "visible")) |
795 | + self.assertTrue(rec_cat_panel.recommended_for_you_content.get_children( |
796 | + )[0].title.get_property("visible")) |
797 | + do_events() |
798 | # test clicking recommended_for_you More button |
799 | - subcat_view.connect("category-selected", self._on_category_selected) |
800 | + self.subcat_view.connect( |
801 | + "category-selected", self._on_category_selected) |
802 | rec_cat_panel.more.clicked() |
803 | - self._p() |
804 | + # this is delayed for some reason so we need to sleep here |
805 | + do_events_with_sleep() |
806 | self.assertNotEqual(self._cat, None) |
807 | self.assertEqual(self._cat.name, "Recommended For You in Internet") |
808 | - win.destroy() |
809 | - |
810 | - def _p(self): |
811 | - for i in range(5): |
812 | - time.sleep(0.1) |
813 | - while Gtk.events_pending(): |
814 | - Gtk.main_iteration() |
815 | - |
816 | + |
817 | + |
818 | +class ExhibitsTestCase(unittest.TestCase): |
819 | + """The test suite for the exhibits carousel.""" |
820 | + |
821 | + def setUp(self): |
822 | + self.datadir = softwarecenter.paths.datadir |
823 | + self.desktopdir = softwarecenter.paths.APP_INSTALL_PATH |
824 | + self.cache = FakedCache() |
825 | + self.db = StoreDatabase(cache=self.cache) |
826 | + self.lobby = catview_gtk.LobbyViewGtk(datadir=self.datadir, |
827 | + desktopdir=self.desktopdir, cache=self.cache, db=self.db, |
828 | + icons=None, apps_filter=None) |
829 | + self.addCleanup(self.lobby.destroy) |
830 | + |
831 | + def _get_banner_from_lobby(self): |
832 | + return self.lobby.vbox.get_children()[-1].get_child() |
833 | + |
834 | + def test_featured_exhibit_by_default(self): |
835 | + """Show the featured exhibit before querying the remote service.""" |
836 | + self.lobby._append_banner_ads() |
837 | + |
838 | + banner = self._get_banner_from_lobby() |
839 | + self.assertEqual(1, len(banner.exhibits)) |
840 | + self.assertIsInstance(banner.exhibits[0], catview_gtk.FeaturedExhibit) |
841 | + |
842 | + def test_no_exhibit_if_not_available(self): |
843 | + """The exhibit should not be shown if the package is not available.""" |
844 | + exhibit = Mock() |
845 | + exhibit.package_names = u'foobarbaz' |
846 | + |
847 | + sca = ObjectWithSignals() |
848 | + sca.query_exhibits = lambda: sca.emit('exhibits', sca, [exhibit]) |
849 | + |
850 | + with patch.object(catview_gtk, 'SoftwareCenterAgent', lambda: sca): |
851 | + self.lobby._append_banner_ads() |
852 | + |
853 | + banner = self._get_banner_from_lobby() |
854 | + self.assertEqual(1, len(banner.exhibits)) |
855 | + self.assertIsInstance(banner.exhibits[0], catview_gtk.FeaturedExhibit) |
856 | + |
857 | + def test_exhibit_if_available(self): |
858 | + """The exhibit should be shown if the package is not available.""" |
859 | + exhibit = Mock() |
860 | + exhibit.package_names = u'foobarbaz' |
861 | + exhibit.banner_url = 'banner' |
862 | + |
863 | + pkg = Mock() |
864 | + pkg.banner_url = '' |
865 | + pkg.title_translated = '' |
866 | + self.cache[u'foobarbaz'] = pkg |
867 | + |
868 | + sca = ObjectWithSignals() |
869 | + sca.query_exhibits = lambda: sca.emit('exhibits', sca, [exhibit]) |
870 | + |
871 | + with patch.object(catview_gtk, 'SoftwareCenterAgent', lambda: sca): |
872 | + self.lobby._append_banner_ads() |
873 | + |
874 | + banner = self._get_banner_from_lobby() |
875 | + self.assertEqual(1, len(banner.exhibits)) |
876 | + self.assertIs(banner.exhibits[0], exhibit) |
877 | |
878 | |
879 | if __name__ == "__main__": |