Merge lp:~mvo/software-center/appdetailsview-cleanup into lp:software-center

Proposed by Michael Vogt
Status: Merged
Merged at revision: 2576
Proposed branch: lp:~mvo/software-center/appdetailsview-cleanup
Merge into: lp:software-center
Diff against target: 1039 lines (+359/-95)
7 files modified
po/POTFILES.in (+0/-1)
softwarecenter/ui/gtk3/panes/softwarepane.py (+1/-3)
softwarecenter/ui/gtk3/views/appdetailsview.py (+234/-81)
test/gen-coverage-for-single-test.py (+4/-0)
test/gtk3/test_appdetailsview.py (+118/-8)
test/gtk3/test_install_progress.py (+1/-1)
test/gtk3/test_views.py (+1/-1)
To merge this branch: bzr merge lp:~mvo/software-center/appdetailsview-cleanup
Reviewer Review Type Date Requested Status
Gary Lasker (community) Approve
Michael Vogt Pending
Review via email: mp+83845@code.launchpad.net

Description of the change

This branch merges appdetailsview and appdetailsview_gtk. It also adds more test coverage for the detailsview.

To post a comment you must log in.
Revision history for this message
Gary Lasker (gary-lasker) wrote :

Very nice, mvo! Thanks for this!

review: Approve

Preview Diff

[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
=== modified file 'po/POTFILES.in'
--- po/POTFILES.in 2011-11-07 09:41:23 +0000
+++ po/POTFILES.in 2011-11-29 19:57:26 +0000
@@ -47,7 +47,6 @@
47softwarecenter/ui/gtk3/panes/unused__channelpane.py47softwarecenter/ui/gtk3/panes/unused__channelpane.py
48softwarecenter/ui/gtk3/panes/viewswitcher.py48softwarecenter/ui/gtk3/panes/viewswitcher.py
49softwarecenter/ui/gtk3/views/appdetailsview.py49softwarecenter/ui/gtk3/views/appdetailsview.py
50softwarecenter/ui/gtk3/views/appdetailsview_gtk.py
51softwarecenter/ui/gtk3/views/appview.py50softwarecenter/ui/gtk3/views/appview.py
52softwarecenter/ui/gtk3/views/catview_gtk.py51softwarecenter/ui/gtk3/views/catview_gtk.py
53softwarecenter/ui/gtk3/views/purchaseview.py52softwarecenter/ui/gtk3/views/purchaseview.py
5453
=== modified file 'softwarecenter/ui/gtk3/panes/softwarepane.py'
--- softwarecenter/ui/gtk3/panes/softwarepane.py 2011-10-25 18:38:08 +0000
+++ softwarecenter/ui/gtk3/panes/softwarepane.py 2011-11-29 19:57:26 +0000
@@ -51,9 +51,7 @@
51from softwarecenter.ui.gtk3.widgets.searchaid import SearchAid51from softwarecenter.ui.gtk3.widgets.searchaid import SearchAid
5252
53from softwarecenter.ui.gtk3.views.appview import AppView53from softwarecenter.ui.gtk3.views.appview import AppView
54from softwarecenter.ui.gtk3.views.appdetailsview_gtk import (54from softwarecenter.ui.gtk3.views.appdetailsview import AppDetailsView
55 AppDetailsViewGtk as
56 AppDetailsView)
5755
58from softwarecenter.utils import is_no_display_desktop_file56from softwarecenter.utils import is_no_display_desktop_file
5957
6058
=== removed file 'softwarecenter/ui/gtk3/views/appdetailsview.py'
--- softwarecenter/ui/gtk3/views/appdetailsview.py 2011-11-09 10:09:16 +0000
+++ softwarecenter/ui/gtk3/views/appdetailsview.py 1970-01-01 00:00:00 +0000
@@ -1,139 +0,0 @@
1# Copyright (C) 2009 Canonical
2#
3# Authors:
4# Michael Vogt
5#
6# This program is free software; you can redistribute it and/or modify it under
7# the terms of the GNU General Public License as published by the Free Software
8# Foundation; version 3.
9#
10# This program is distributed in the hope that it will be useful, but WITHOUT
11# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
12# FOR A PARTICULAR PURPOSE. See the GNU General Public License for more
13# details.
14#
15# You should have received a copy of the GNU General Public License along with
16# this program; if not, write to the Free Software Foundation, Inc.,
17# 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
18
19import logging
20import softwarecenter.ui.gtk3.dialogs as dialogs
21
22from gettext import gettext as _
23
24from softwarecenter.db.application import AppDetails
25from softwarecenter.backend.reviews import get_review_loader
26from softwarecenter.backend import get_install_backend
27
28
29LOG=logging.getLogger(__name__)
30
31class AppDetailsViewBase(object):
32
33 def __init__(self, db, distro, icons, cache, datadir):
34 self.db = db
35 self.distro = distro
36 self.icons = icons
37 self.cache = cache
38 self.backend = get_install_backend()
39 self.cache.connect("cache-ready", self._on_cache_ready)
40 self.datadir = datadir
41 self.app = None
42 self.appdetails = None
43 self.addons_to_install = []
44 self.addons_to_remove = []
45 # reviews
46 self.review_loader = get_review_loader(self.cache, self.db)
47 # aptdaemon
48
49 def _draw(self):
50 """ draw the current app into the window, maybe the function
51 you need to overwrite
52 """
53 pass
54
55 # public API
56 def show_app(self, app):
57 """ show the given application """
58 if app is None:
59 return
60 self.app = app
61 self.appdetails = AppDetails(self.db, application=app)
62 #print "AppDetailsViewWebkit:"
63 #print self.appdetails
64 self._draw()
65 self._check_for_reviews()
66 self.emit("selected", self.app)
67
68 def refresh_app(self):
69 self.show_app(self.app)
70
71 # common code
72 def _review_write_new(self):
73 if (not self.app or
74 not self.app.pkgname in self.cache or
75 not self.cache[self.app.pkgname].candidate):
76 dialogs.error(None,
77 _("Version unknown"),
78 _("The version of the application can not "
79 "be detected. Entering a review is not "
80 "possible."))
81 return
82 # gather data
83 pkg = self.cache[self.app.pkgname]
84 version = pkg.candidate.version
85 origin = self.cache.get_origin(self.app.pkgname)
86
87 # FIXME: probably want to not display the ui if we can't review it
88 if not origin:
89 dialogs.error(None,
90 _("Origin unknown"),
91 _("The origin of the application can not "
92 "be detected. Entering a review is not "
93 "possible."))
94 return
95
96 if pkg.installed:
97 version = pkg.installed.version
98 # call the loader to do call out the right helper and collect the result
99 parent_xid = ''
100 #parent_xid = get_parent_xid(self)
101 self.review_loader.spawn_write_new_review_ui(
102 self.app, version, self.appdetails.icon, origin,
103 parent_xid, self.datadir,
104 self._reviews_ready_callback)
105
106 def _review_report_abuse(self, review_id):
107 parent_xid = ''
108 #parent_xid = get_parent_xid(self)
109 self.review_loader.spawn_report_abuse_ui(
110 review_id, parent_xid, self.datadir, self._reviews_ready_callback)
111
112 def _review_submit_usefulness(self, review_id, is_useful):
113 parent_xid = ''
114 #parent_xid = get_parent_xid(self)
115 self.review_loader.spawn_submit_usefulness_ui(
116 review_id, is_useful, parent_xid, self.datadir,
117 self._reviews_ready_callback)
118
119 def _review_modify(self, review_id):
120 parent_xid = ''
121 #parent_xid = get_parent_xid(self)
122 self.review_loader.spawn_modify_review_ui(
123 parent_xid, self.appdetails.icon, self.datadir, review_id,
124 self._reviews_ready_callback)
125
126 def _review_delete(self, review_id):
127 parent_xid = ''
128 #parent_xid = get_parent_xid(self)
129 self.review_loader.spawn_delete_review_ui(
130 review_id, parent_xid, self.datadir, self._reviews_ready_callback)
131
132 # internal callbacks
133 def _on_cache_ready(self, cache):
134 # re-show the application if the cache changes, it may affect the
135 # current application
136 LOG.debug("on_cache_ready")
137 self.show_app(self.app)
138
139
1400
=== renamed file 'softwarecenter/ui/gtk3/views/appdetailsview_gtk.py' => 'softwarecenter/ui/gtk3/views/appdetailsview.py'
--- softwarecenter/ui/gtk3/views/appdetailsview_gtk.py 2011-11-11 03:05:45 +0000
+++ softwarecenter/ui/gtk3/views/appdetailsview.py 2011-11-29 19:57:26 +0000
@@ -1,7 +1,8 @@
1# -*- coding: utf-8 -*-1# -*- coding: utf-8 -*-
2# Copyright (C) 2009 Canonical2# Copyright (C) 2009-2011 Canonical
3#3#
4# Authors:4# Authors:
5# Matthew McGowan
5# Michael Vogt6# Michael Vogt
6#7#
7# This program is free software; you can redistribute it and/or modify it under8# This program is free software; you can redistribute it and/or modify it under
@@ -17,8 +18,6 @@
17# this program; if not, write to the Free Software Foundation, Inc.,18# this program; if not, write to the Free Software Foundation, Inc.,
18# 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA19# 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
1920
20import gi
21gi.require_version("Gtk", "3.0")
22from gi.repository import Atk, Gtk, Gdk, GObject, GdkPixbuf, Pango21from gi.repository import Atk, Gtk, Gdk, GObject, GdkPixbuf, Pango
2322
24import datetime23import datetime
@@ -49,7 +48,6 @@
49from softwarecenter.distro import get_distro48from softwarecenter.distro import get_distro
50from softwarecenter.backend.weblive import get_weblive_backend49from softwarecenter.backend.weblive import get_weblive_backend
51from softwarecenter.ui.gtk3.dialogs import error50from softwarecenter.ui.gtk3.dialogs import error
52from appdetailsview import AppDetailsViewBase
5351
54from softwarecenter.ui.gtk3.em import StockEms, em52from softwarecenter.ui.gtk3.em import StockEms, em
55from softwarecenter.ui.gtk3.drawing import color_to_hex53from softwarecenter.ui.gtk3.drawing import color_to_hex
@@ -65,13 +63,21 @@
65 ShowWebLiveServerChooserDialog)63 ShowWebLiveServerChooserDialog)
66from softwarecenter.ui.gtk3.gmenusearch import GMenuSearcher64from softwarecenter.ui.gtk3.gmenusearch import GMenuSearcher
6765
68LOG = logging.getLogger(__name__)66import softwarecenter.ui.gtk3.dialogs as dialogs
6967
68from softwarecenter.backend.reviews import get_review_loader
69from softwarecenter.backend import get_install_backend
70
71
72LOG=logging.getLogger(__name__)
7073
71class StatusBar(Gtk.Alignment):74class StatusBar(Gtk.Alignment):
75 """ Subclass of Gtk.Alignment that draws a small dash border
76 around the rectangle.
77 """
7278
73 def __init__(self, view):79 def __init__(self, view):
74 GObject.GObject.__init__(self, xscale=1.0, yscale=1.0)80 Gtk.Alignment.__init__(self, xscale=1.0, yscale=1.0)
75 self.set_padding(StockEms.SMALL, StockEms.SMALL, 0, 0)81 self.set_padding(StockEms.SMALL, StockEms.SMALL, 0, 0)
7682
77 self.hbox = Gtk.HBox()83 self.hbox = Gtk.HBox()
@@ -114,11 +120,15 @@
114 cr.stroke()120 cr.stroke()
115121
116 cr.restore()122 cr.restore()
117 for child in self: self.propagate_draw(child, cr)123 for child in self:
124 self.propagate_draw(child, cr)
118125
119126
120class PackageStatusBar(StatusBar):127class PackageStatusBar(StatusBar):
121 128 """ Package specific status bar that contains a state label,
129 a action button and a progress bar.
130 """
131
122 def __init__(self, view):132 def __init__(self, view):
123 StatusBar.__init__(self, view)133 StatusBar.__init__(self, view)
124 self.installed_icon = Gtk.Image.new_from_icon_name(134 self.installed_icon = Gtk.Image.new_from_icon_name(
@@ -139,6 +149,8 @@
139 self.hbox.pack_end(self.progress, False, False, 0)149 self.hbox.pack_end(self.progress, False, False, 0)
140 self.show_all()150 self.show_all()
141151
152 self.app_manager = get_appmanager()
153
142 self.button.connect('clicked', self._on_button_clicked)154 self.button.connect('clicked', self._on_button_clicked)
143 GObject.timeout_add(500, self._pulse_helper)155 GObject.timeout_add(500, self._pulse_helper)
144156
@@ -152,9 +164,9 @@
152 button.set_sensitive(False)164 button.set_sensitive(False)
153 state = self.pkg_state165 state = self.pkg_state
154 app = self.view.app166 app = self.view.app
155 app_manager = get_appmanager()
156 addons_to_install = self.view.addons_manager.addons_to_install167 addons_to_install = self.view.addons_manager.addons_to_install
157 addons_to_remove = self.view.addons_manager.addons_to_remove168 addons_to_remove = self.view.addons_manager.addons_to_remove
169 app_manager = self.app_manager
158 if state == PkgStates.INSTALLED:170 if state == PkgStates.INSTALLED:
159 app_manager.remove(171 app_manager.remove(
160 app, addons_to_install, addons_to_remove)172 app, addons_to_install, addons_to_remove)
@@ -204,6 +216,7 @@
204 self.button.show()216 self.button.show()
205 self.show()217 self.show()
206 else:218 else:
219 # mvo: why do we override state here again?
207 state = app_details.pkg_state220 state = app_details.pkg_state
208 self.pkg_state = state221 self.pkg_state = state
209 self.button.set_sensitive(True)222 self.button.set_sensitive(True)
@@ -212,9 +225,10 @@
212 self.progress.hide()225 self.progress.hide()
213 self.installed_icon.hide()226 self.installed_icon.hide()
214227
215 # FIXME: Use a Gtk.Action for the Install/Remove/Buy/Add Source/Update Now action228 # FIXME: Use a Gtk.Action for the Install/Remove/Buy/Add
216 # so that all UI controls (menu item, applist view button and appdetails229 # Source/Update Now action so that all UI controls
217 # view button) are managed centrally: button text, button sensitivity,230 # (menu item, applist view button and appdetails view button)
231 # are managed centrally: button text, button sensitivity,
218 # and the associated callback.232 # and the associated callback.
219 if state == PkgStates.INSTALLING:233 if state == PkgStates.INSTALLING:
220 self.set_label(_('Installing...'))234 self.set_label(_('Installing...'))
@@ -236,18 +250,23 @@
236 self.set_label(_("Installed (you're using it right now)"))250 self.set_label(_("Installed (you're using it right now)"))
237 else:251 else:
238 if app_details.purchase_date:252 if app_details.purchase_date:
239 # purchase_date is a string, must first convert to datetime.datetime253 # purchase_date is a string, must first convert to
240 pdate = self._convert_purchase_date_str_to_datetime(app_details.purchase_date)254 # datetime.datetime
241 # TRANSLATORS : %Y-%m-%d formats the date as 2011-03-31, please specify a format per your255 pdate = self._convert_purchase_date_str_to_datetime(
242 # locale (if you prefer, %x can be used to provide a default locale-specific date 256 app_details.purchase_date)
257 # TRANSLATORS : %Y-%m-%d formats the date as 2011-03-31,
258 # please specify a format per your locale (if you prefer,
259 # %x can be used to provide a default locale-specific date
243 # representation)260 # representation)
244 self.set_label(pdate.strftime(_('Purchased on %Y-%m-%d')))261 self.set_label(pdate.strftime(_('Purchased on %Y-%m-%d')))
245 elif app_details.installation_date:262 elif app_details.installation_date:
246 # TRANSLATORS : %Y-%m-%d formats the date as 2011-03-31, please specify a format per your263 # TRANSLATORS : %Y-%m-%d formats the date as 2011-03-31,
247 # locale (if you prefer, %x can be used to provide a default locale-specific date 264 # please specify a format per your locale (if you prefer,
265 # %x can be used to provide a default locale-specific date
248 # representation)266 # representation)
249 template = _('Installed on %Y-%m-%d')267 template = _('Installed on %Y-%m-%d')
250 self.set_label(app_details.installation_date.strftime(template))268 self.set_label(app_details.installation_date.strftime(
269 template))
251 else:270 else:
252 self.set_label(_('Installed'))271 self.set_label(_('Installed'))
253 if state == PkgStates.REINSTALLABLE: # only deb files atm272 if state == PkgStates.REINSTALLABLE: # only deb files atm
@@ -256,22 +275,26 @@
256 self.set_button_label(_('Remove'))275 self.set_button_label(_('Remove'))
257 elif state == PkgStates.NEEDS_PURCHASE:276 elif state == PkgStates.NEEDS_PURCHASE:
258 # FIXME: need to determine the currency dynamically once we can277 # FIXME: need to determine the currency dynamically once we can
259 # get that info from the software-center-agent/payments service.278 # get that info from the software-center-agent/payments
260 # NOTE: the currency string for this label is purposely not translatable279 # service.
261 # when hardcoded, since it (currently) won't vary based on locale280 # NOTE: the currency string for this label is purposely not
262 # and as such we don't want it translated281 # translatable when hardcoded, since it (currently)
282 # won't vary based on locale and as such we don't want
283 # it translated
263 self.set_label("US$ %s" % app_details.price)284 self.set_label("US$ %s" % app_details.price)
264 self.set_button_label(_(u'Buy\u2026'))285 self.set_button_label(_(u'Buy\u2026'))
265 elif state == PkgStates.PURCHASED_BUT_REPO_MUST_BE_ENABLED:286 elif state == PkgStates.PURCHASED_BUT_REPO_MUST_BE_ENABLED:
266 # purchase_date is a string, must first convert to datetime.datetime287 # purchase_date is a string, must first convert to datetime.datetime
267 pdate = self._convert_purchase_date_str_to_datetime(app_details.purchase_date)288 pdate = self._convert_purchase_date_str_to_datetime(
268 # TRANSLATORS : %Y-%m-%d formats the date as 2011-03-31, please specify a format per your289 app_details.purchase_date)
269 # locale (if you prefer, %x can be used to provide a default locale-specific date 290 # TRANSLATORS : %Y-%m-%d formats the date as 2011-03-31, please
270 # representation)291 # specify a format per your locale (if you prefer, %x can be used
292 # to provide a default locale-specific date representation)
271 self.set_label(pdate.strftime(_('Purchased on %Y-%m-%d')))293 self.set_label(pdate.strftime(_('Purchased on %Y-%m-%d')))
272 self.set_button_label(_('Install'))294 self.set_button_label(_('Install'))
273 elif state == PkgStates.UNINSTALLED:295 elif state == PkgStates.UNINSTALLED:
274 #special label only if the app being viewed is software centre itself296 #special label only if the app being viewed is software centre
297 # itself
275 if app_details.pkgname== SOFTWARE_CENTER_PKGNAME:298 if app_details.pkgname== SOFTWARE_CENTER_PKGNAME:
276 self.set_label(_("Removed (close it and it'll be gone)"))299 self.set_label(_("Removed (close it and it'll be gone)"))
277 else:300 else:
@@ -296,8 +319,9 @@
296 self.set_button_label(_("Install"))319 self.set_button_label(_("Install"))
297 self.set_label("")320 self.set_label("")
298 elif state == PkgStates.NOT_FOUND:321 elif state == PkgStates.NOT_FOUND:
299 # this is used when the pkg is not in the cache and there is no request322 # this is used when the pkg is not in the cache and there is no
300 # we display the error in the summary field and hide the rest323 # request we display the error in the summary field and hide the
324 # rest
301 pass325 pass
302 elif state == PkgStates.NEEDS_SOURCE:326 elif state == PkgStates.NEEDS_SOURCE:
303 channelfile = self.app_details.channelfile327 channelfile = self.app_details.channelfile
@@ -324,13 +348,16 @@
324 348
325 def _convert_purchase_date_str_to_datetime(self, purchase_date):349 def _convert_purchase_date_str_to_datetime(self, purchase_date):
326 if purchase_date is not None:350 if purchase_date is not None:
327 return datetime.datetime.strptime(purchase_date, "%Y-%m-%d %H:%M:%S")351 return datetime.datetime.strptime(
352 purchase_date, "%Y-%m-%d %H:%M:%S")
328353
329354
330class PackageInfo(Gtk.HBox):355class PackageInfo(Gtk.HBox):
356 """ Box with labels for package specific information like version info
357 """
331358
332 def __init__(self, key, info_keys):359 def __init__(self, key, info_keys):
333 GObject.GObject.__init__(self)360 Gtk.HBox.__init__(self)
334 self.set_spacing(StockEms.LARGE)361 self.set_spacing(StockEms.LARGE)
335362
336 self.key = key363 self.key = key
@@ -390,7 +417,7 @@
390 """ Widget to select addons: CheckButton - Icon - Title (pkgname) """417 """ Widget to select addons: CheckButton - Icon - Title (pkgname) """
391418
392 def __init__(self, db, icons, pkgname):419 def __init__(self, db, icons, pkgname):
393 GObject.GObject.__init__(self)420 Gtk.HBox.__init__(self)
394 self.set_spacing(StockEms.SMALL)421 self.set_spacing(StockEms.SMALL)
395 self.set_border_width(2)422 self.set_border_width(2)
396423
@@ -434,13 +461,14 @@
434 color = color_to_hex(context.get_color(Gtk.StateFlags.NORMAL))461 color = color_to_hex(context.get_color(Gtk.StateFlags.NORMAL))
435 context.restore()462 context.restore()
436463
437 self.title.set_markup(title + ' <span color="%s">(%s)</span>' % (color, pkgname))464 self.title.set_markup(
465 title + ' <span color="%s">(%s)</span>' % (color, pkgname))
438 self.title.set_alignment(0.0, 0.5)466 self.title.set_alignment(0.0, 0.5)
439 self.title.set_line_wrap(True)467 self.title.set_line_wrap(True)
440 self.title.set_ellipsize(Pango.EllipsizeMode.END)468 self.title.set_ellipsize(Pango.EllipsizeMode.END)
441 hbox.pack_start(self.title, False, False, 0)469 hbox.pack_start(self.title, False, False, 0)
442470
443 loader = self.get_ancestor(AppDetailsViewGtk).review_loader471 loader = self.get_ancestor(AppDetailsView).review_loader
444 stats = loader.get_review_stats(self.app)472 stats = loader.get_review_stats(self.app)
445 if stats != None:473 if stats != None:
446 rating = Star()474 rating = Star()
@@ -471,7 +499,7 @@
471 }499 }
472500
473 def __init__(self, addons_manager):501 def __init__(self, addons_manager):
474 GObject.GObject.__init__(self)502 Gtk.VBox.__init__(self)
475 self.set_spacing(12)503 self.set_spacing(12)
476504
477 self.addons_manager = addons_manager505 self.addons_manager = addons_manager
@@ -532,6 +560,10 @@
532560
533561
534class AddonsStatusBar(StatusBar):562class AddonsStatusBar(StatusBar):
563 """ Statusbar for the addons.
564 This will become visible if any addons are scheduled for install
565 or remove.
566 """
535 567
536 def __init__(self, addons_manager):568 def __init__(self, addons_manager):
537 StatusBar.__init__(self, addons_manager.view)569 StatusBar.__init__(self, addons_manager.view)
@@ -576,10 +608,19 @@
576 self.view.addons_to_remove = self.addons_manager.addons_to_remove608 self.view.addons_to_remove = self.addons_manager.addons_to_remove
577 LOG.debug("ApplyButtonClicked: inst=%s rm=%s" % (609 LOG.debug("ApplyButtonClicked: inst=%s rm=%s" % (
578 self.view.addons_to_install, self.view.addons_to_remove))610 self.view.addons_to_install, self.view.addons_to_remove))
579 AppDetailsViewBase.apply_changes(self.view)611 # apply
580612 app_manager = get_appmanager()
581613 app_manager.apply_changes(self.view.app,
582class AddonsManager():614 self.view.addons_to_install,
615 self.view.addons_to_remove)
616
617
618class AddonsManager(object):
619 """ Addons manager component.
620 This component deals with keeping track of what is marked
621 for install or removal.
622 """
623
583 def __init__(self, view):624 def __init__(self, view):
584 self.view = view625 self.view = view
585626
@@ -627,8 +668,7 @@
627668
628669
629_asset_cache = {}670_asset_cache = {}
630class AppDetailsViewGtk(Viewport, AppDetailsViewBase):671class AppDetailsView(Viewport):
631
632 """ The view that shows the application details """672 """ The view that shows the application details """
633673
634 # the size of the icon on the left side674 # the size of the icon on the left side
@@ -638,7 +678,8 @@
638 "ui/gtk3/art/itemview-background.png")678 "ui/gtk3/art/itemview-background.png")
639679
640680
641 # need to include application-request-action here also since we are multiple-inheriting681 # need to include application-request-action here also since we are
682 # multiple-inheriting
642 __gsignals__ = {'selected':(GObject.SignalFlags.RUN_FIRST,683 __gsignals__ = {'selected':(GObject.SignalFlags.RUN_FIRST,
643 None,684 None,
644 (GObject.TYPE_PYOBJECT,)),685 (GObject.TYPE_PYOBJECT,)),
@@ -649,10 +690,24 @@
649690
650691
651 def __init__(self, db, distro, icons, cache, datadir, pane):692 def __init__(self, db, distro, icons, cache, datadir, pane):
652 AppDetailsViewBase.__init__(self, db, distro, icons, cache, datadir)
653 Viewport.__init__(self)693 Viewport.__init__(self)
694 # basic stuff
695 self.db = db
696 self.distro = distro
697 self.icons = icons
698 self.cache = cache
699 self.backend = get_install_backend()
700 self.cache.connect("cache-ready", self._on_cache_ready)
701 self.datadir = datadir
702 self.app = None
703 self.appdetails = None
704 self.addons_to_install = []
705 self.addons_to_remove = []
706 # reviews
707 self.review_loader = get_review_loader(self.cache, self.db)
708
709 # ui specific stuff
654 self.set_shadow_type(Gtk.ShadowType.NONE)710 self.set_shadow_type(Gtk.ShadowType.NONE)
655
656 self.set_name("view")711 self.set_name("view")
657712
658 self._pane = pane713 self._pane = pane
@@ -671,10 +726,15 @@
671 self.a11y.set_name("app_details pane")726 self.a11y.set_name("app_details pane")
672727
673 # aptdaemon728 # aptdaemon
674 self.backend.connect("transaction-started", self._on_transaction_started)729 self.backend.connect(
675 self.backend.connect("transaction-stopped", self._on_transaction_stopped)730 "transaction-started", self._on_transaction_started)
676 self.backend.connect("transaction-finished", self._on_transaction_finished)731 self.backend.connect(
677 self.backend.connect("transaction-progress-changed", self._on_transaction_progress_changed)732 "transaction-stopped", self._on_transaction_stopped)
733 self.backend.connect(
734 "transaction-finished", self._on_transaction_finished)
735 self.backend.connect(
736 "transaction-progress-changed",
737 self._on_transaction_progress_changed)
678738
679 # network status watcher739 # network status watcher
680 watcher = get_network_watcher()740 watcher = get_network_watcher()
@@ -733,7 +793,8 @@
733 return793 return
734794
735 def _check_for_reviews(self):795 def _check_for_reviews(self):
736 # self.app may be undefined on network state change events (LP: #742635)796 # self.app may be undefined on network state change events
797 # (LP: #742635)
737 if not self.app:798 if not self.app:
738 return799 return
739 # review stats is fast and syncronous800 # review stats is fast and syncronous
@@ -801,7 +862,8 @@
801 # stats we update manually862 # stats we update manually
802 old_stats = self.review_loader.get_review_stats(self.app)863 old_stats = self.review_loader.get_review_stats(self.app)
803 if ((old_stats is None and len(reviews_data) > 0) or864 if ((old_stats is None and len(reviews_data) > 0) or
804 (old_stats is not None and old_stats.ratings_total < len(reviews_data))):865 (old_stats is not None and
866 old_stats.ratings_total < len(reviews_data))):
805 # generate new stats867 # generate new stats
806 stats = ReviewStats(app)868 stats = ReviewStats(app)
807 stats.ratings_total = len(reviews_data)869 stats.ratings_total = len(reviews_data)
@@ -865,9 +927,13 @@
865 servers = self.weblive.get_servers_for_pkgname(self.app.pkgname)927 servers = self.weblive.get_servers_for_pkgname(self.app.pkgname)
866928
867 if len(servers) == 0:929 if len(servers) == 0:
868 error(None,"No available server", "There is currently no available WebLive server for this application.\nPlease try again later.")930 error(None,
931 "No available server",
932 "There is currently no available WebLive server "
933 "for this application.\nPlease try again later.")
869 elif len(servers) == 1:934 elif len(servers) == 1:
870 self.weblive.create_automatic_user_and_run_session(session=cmd,serverid=servers[0].name)935 self.weblive.create_automatic_user_and_run_session(
936 session=cmd,serverid=servers[0].name)
871 button.set_sensitive(False)937 button.set_sensitive(False)
872 else:938 else:
873 d = ShowWebLiveServerChooserDialog(servers, self.app.pkgname)939 d = ShowWebLiveServerChooserDialog(servers, self.app.pkgname)
@@ -880,7 +946,8 @@
880 d.destroy()946 d.destroy()
881947
882 if serverid:948 if serverid:
883 self.weblive.create_automatic_user_and_run_session(session=cmd,serverid=serverid)949 self.weblive.create_automatic_user_and_run_session(
950 session=cmd, serverid=serverid)
884 button.set_sensitive(False)951 button.set_sensitive(False)
885952
886 elif self.weblive.client.state == "connected":953 elif self.weblive.client.state == "connected":
@@ -937,7 +1004,8 @@
9371004
938 # star rating widget1005 # star rating widget
939 self.review_stats_widget = StarRatingsWidget()1006 self.review_stats_widget = StarRatingsWidget()
940 vb_inner.pack_start(self.review_stats_widget, False, False, StockEms.SMALL)1007 vb_inner.pack_start(
1008 self.review_stats_widget, False, False, StockEms.SMALL)
9411009
942 #~ vb_inner.set_property("can-focus", True)1010 #~ vb_inner.set_property("can-focus", True)
943 self.title.a11y = vb_inner.get_accessible()1011 self.title.a11y = vb_inner.get_accessible()
@@ -985,7 +1053,8 @@
985 # attach to all the WebLive events1053 # attach to all the WebLive events
986 self.weblive.client.connect("progress", self.on_weblive_progress)1054 self.weblive.client.connect("progress", self.on_weblive_progress)
987 self.weblive.client.connect("connected", self.on_weblive_connected)1055 self.weblive.client.connect("connected", self.on_weblive_connected)
988 self.weblive.client.connect("disconnected", self.on_weblive_disconnected)1056 self.weblive.client.connect(
1057 "disconnected", self.on_weblive_disconnected)
989 self.weblive.client.connect("exception", self.on_weblive_exception)1058 self.weblive.client.connect("exception", self.on_weblive_exception)
990 self.weblive.client.connect("warning", self.on_weblive_warning)1059 self.weblive.client.connect("warning", self.on_weblive_warning)
9911060
@@ -1053,9 +1122,12 @@
1053 self.reviews.connect("submit-usefulness", self._on_review_submit_usefulness)1122 self.reviews.connect("submit-usefulness", self._on_review_submit_usefulness)
1054 self.reviews.connect("modify-review", self._on_review_modify)1123 self.reviews.connect("modify-review", self._on_review_modify)
1055 self.reviews.connect("delete-review", self._on_review_delete)1124 self.reviews.connect("delete-review", self._on_review_delete)
1056 self.reviews.connect("more-reviews-clicked", self._on_more_reviews_clicked)1125 self.reviews.connect("more-reviews-clicked",
1057 self.reviews.connect("different-review-language-clicked", self._on_reviews_in_different_language_clicked)1126 self._on_more_reviews_clicked)
1058 self.reviews.connect("review-sort-changed", self._on_review_sort_method_changed)1127 self.reviews.connect("different-review-language-clicked",
1128 self._on_reviews_in_different_language_clicked)
1129 self.reviews.connect("review-sort-changed",
1130 self._on_review_sort_method_changed)
1059 if get_distro().REVIEWS_SERVER:1131 if get_distro().REVIEWS_SERVER:
1060 vb.pack_start(self.reviews, False, False, 0)1132 vb.pack_start(self.reviews, False, False, 0)
10611133
@@ -1144,7 +1216,8 @@
1144 # show or hide the homepage button and set uri if homepage specified1216 # show or hide the homepage button and set uri if homepage specified
1145 if app_details.website:1217 if app_details.website:
1146 self.homepage_btn.show()1218 self.homepage_btn.show()
1147 self.homepage_btn.set_markup("<a href=\"%s\">%s</a>"%(self.app_details.website, _('Developer Web Site')))1219 self.homepage_btn.set_markup("<a href=\"%s\">%s</a>" % (
1220 self.app_details.website, _('Developer Web Site')))
1148 self.homepage_btn.set_tooltip_text(app_details.website)1221 self.homepage_btn.set_tooltip_text(app_details.website)
1149 else:1222 else:
1150 self.homepage_btn.hide()1223 self.homepage_btn.hide()
@@ -1181,7 +1254,8 @@
1181 if app_details.version:1254 if app_details.version:
1182 version = '%s %s' % (app_details.pkgname, app_details.version)1255 version = '%s %s' % (app_details.pkgname, app_details.version)
1183 else:1256 else:
1184 version = utf8(_("%s (unknown version)")) % utf8(app_details.pkgname)1257 version = utf8(_("%s (unknown version)")) % utf8(
1258 app_details.pkgname)
1185 if app_details.license:1259 if app_details.license:
1186 license = app_details.license1260 license = app_details.license
1187 else:1261 else:
@@ -1240,7 +1314,7 @@
12401314
1241 self._update_layout_error_status(pkg_ambiguous_error)1315 self._update_layout_error_status(pkg_ambiguous_error)
1242 self._update_title_markup(appname, summary)1316 self._update_title_markup(appname, summary)
1243 self._update_app_icon(app_details)1317
1244 self._update_app_description(app_details, app_details.pkgname)1318 self._update_app_description(app_details, app_details.pkgname)
1245 self._update_description_footer_links(app_details)1319 self._update_description_footer_links(app_details)
1246 self._update_app_screenshot(app_details)1320 self._update_app_screenshot(app_details)
@@ -1306,7 +1380,11 @@
1306 vb.pack_start(cmd_label, False, False, 0)1380 vb.pack_start(cmd_label, False, False, 0)
1307 self.installed_where_hbox.show_all()1381 self.installed_where_hbox.show_all()
13081382
1309 def _add_where_is_it_launcher(self, where):1383 def _add_where_is_it_launcher(self, desktop_file):
1384 searcher = GMenuSearcher()
1385 where = searcher.get_main_menu_path(desktop_file)
1386 if not where:
1387 return
1310 # display launcher location1388 # display launcher location
1311 label = Gtk.Label(label=_("Find it in the menu: "))1389 label = Gtk.Label(label=_("Find it in the menu: "))
1312 self.installed_where_hbox.pack_start(label, False, False, 0)1390 self.installed_where_hbox.pack_start(label, False, False, 0)
@@ -1382,23 +1460,18 @@
1382 # first try the desktop file from the DB, then see if1460 # first try the desktop file from the DB, then see if
1383 # there is a local desktop file with the same name as 1461 # there is a local desktop file with the same name as
1384 # the package1462 # the package
1385 searcher = GMenuSearcher()
1386 # try to show menu location if there is a desktop file, but1463 # try to show menu location if there is a desktop file, but
1387 # never show commandline programs for apps with a desktop file1464 # never show commandline programs for apps with a desktop file
1388 # to cover cases like "file-roller" that have NoDisplay=true1465 # to cover cases like "file-roller" that have NoDisplay=true
1389 desktop_file = get_desktop_file()1466 desktop_file = get_desktop_file()
1390 if desktop_file:1467 if desktop_file:
1391 where = searcher.get_main_menu_path(desktop_file)1468 self._add_where_is_it_launcher(desktop_file)
1392 if where:
1393 self._add_where_is_it_launcher(where)
1394 # if there is no desktop file, show commandline1469 # if there is no desktop file, show commandline
1395 else:1470 else:
1396 self._add_where_is_it_commandline(self.app_details.pkgname)1471 self._add_where_is_it_commandline(self.app_details.pkgname)
1397 return1472 return
13981473
1399 # public API1474 # public API
1400 # FIXME: port to AppDetailsViewBase as
1401 # AppDetailsViewBase.show_app(self, app)
1402 def show_app(self, app, force=False):1475 def show_app(self, app, force=False):
1403 LOG.debug("AppDetailsView.show_app '%s'" % app)1476 LOG.debug("AppDetailsView.show_app '%s'" % app)
1404 if app is None:1477 if app is None:
@@ -1442,7 +1515,78 @@
1442 self.emit("selected", self.app)1515 self.emit("selected", self.app)
1443 return1516 return
14441517
1445 # internal callback1518 def refresh_app(self):
1519 self.show_app(self.app)
1520
1521
1522 # common code
1523 def _review_write_new(self):
1524 if (not self.app or
1525 not self.app.pkgname in self.cache or
1526 not self.cache[self.app.pkgname].candidate):
1527 dialogs.error(None,
1528 _("Version unknown"),
1529 _("The version of the application can not "
1530 "be detected. Entering a review is not "
1531 "possible."))
1532 return
1533 # gather data
1534 pkg = self.cache[self.app.pkgname]
1535 version = pkg.candidate.version
1536 origin = self.cache.get_origin(self.app.pkgname)
1537
1538 # FIXME: probably want to not display the ui if we can't review it
1539 if not origin:
1540 dialogs.error(None,
1541 _("Origin unknown"),
1542 _("The origin of the application can not "
1543 "be detected. Entering a review is not "
1544 "possible."))
1545 return
1546
1547 if pkg.installed:
1548 version = pkg.installed.version
1549 # call the loader to do call out the right helper and collect the result
1550 parent_xid = ''
1551 #parent_xid = get_parent_xid(self)
1552 self.review_loader.spawn_write_new_review_ui(
1553 self.app, version, self.appdetails.icon, origin,
1554 parent_xid, self.datadir,
1555 self._reviews_ready_callback)
1556
1557 def _review_report_abuse(self, review_id):
1558 parent_xid = ''
1559 #parent_xid = get_parent_xid(self)
1560 self.review_loader.spawn_report_abuse_ui(
1561 review_id, parent_xid, self.datadir, self._reviews_ready_callback)
1562
1563 def _review_submit_usefulness(self, review_id, is_useful):
1564 parent_xid = ''
1565 #parent_xid = get_parent_xid(self)
1566 self.review_loader.spawn_submit_usefulness_ui(
1567 review_id, is_useful, parent_xid, self.datadir,
1568 self._reviews_ready_callback)
1569
1570 def _review_modify(self, review_id):
1571 parent_xid = ''
1572 #parent_xid = get_parent_xid(self)
1573 self.review_loader.spawn_modify_review_ui(
1574 parent_xid, self.appdetails.icon, self.datadir, review_id,
1575 self._reviews_ready_callback)
1576
1577 def _review_delete(self, review_id):
1578 parent_xid = ''
1579 #parent_xid = get_parent_xid(self)
1580 self.review_loader.spawn_delete_review_ui(
1581 review_id, parent_xid, self.datadir, self._reviews_ready_callback)
1582
1583 # internal callbacks
1584 def _on_cache_ready(self, cache):
1585 # re-show the application if the cache changes, it may affect the
1586 # current application
1587 LOG.debug("on_cache_ready")
1588 self.show_app(self.app)
1589
1446 def _update_interface_on_trans_ended(self, result):1590 def _update_interface_on_trans_ended(self, result):
1447 state = self.pkg_statusbar.pkg_state1591 state = self.pkg_statusbar.pkg_state
14481592
@@ -1483,7 +1627,8 @@
14831627
1484 return False1628 return False
14851629
1486 def _on_transaction_started(self, backend, pkgname, appname, trans_id, trans_type):1630 def _on_transaction_started(self, backend, pkgname, appname, trans_id,
1631 trans_type):
1487 if self.addons_statusbar.applying:1632 if self.addons_statusbar.applying:
1488 self.pkg_statusbar.configure(self.app_details, AppActions.APPLY)1633 self.pkg_statusbar.configure(self.app_details, AppActions.APPLY)
1489 return1634 return
@@ -1491,7 +1636,8 @@
1491 state = self.pkg_statusbar.pkg_state1636 state = self.pkg_statusbar.pkg_state
1492 LOG.debug("_on_transaction_started %s" % state)1637 LOG.debug("_on_transaction_started %s" % state)
1493 if state == PkgStates.NEEDS_PURCHASE:1638 if state == PkgStates.NEEDS_PURCHASE:
1494 self.pkg_statusbar.configure(self.app_details, PkgStates.INSTALLING_PURCHASED)1639 self.pkg_statusbar.configure(self.app_details,
1640 PkgStates.INSTALLING_PURCHASED)
1495 elif state == PkgStates.UNINSTALLED:1641 elif state == PkgStates.UNINSTALLED:
1496 self.pkg_statusbar.configure(self.app_details, PkgStates.INSTALLING)1642 self.pkg_statusbar.configure(self.app_details, PkgStates.INSTALLING)
1497 elif state == PkgStates.INSTALLED:1643 elif state == PkgStates.INSTALLED:
@@ -1501,7 +1647,8 @@
1501 elif state == PkgStates.REINSTALLABLE:1647 elif state == PkgStates.REINSTALLABLE:
1502 self.pkg_statusbar.configure(self.app_details, PkgStates.INSTALLING)1648 self.pkg_statusbar.configure(self.app_details, PkgStates.INSTALLING)
1503 # FIXME: is there a way to tell if we are installing/removing?1649 # FIXME: is there a way to tell if we are installing/removing?
1504 # we will assume that it is being installed, but this means that during removals we get the text "Installing.."1650 # we will assume that it is being installed, but this means that
1651 # during removals we get the text "Installing.."
1505 # self.pkg_statusbar.configure(self.app_details, PkgStates.REMOVING)1652 # self.pkg_statusbar.configure(self.app_details, PkgStates.REMOVING)
1506 return1653 return
15071654
@@ -1516,7 +1663,9 @@
1516 return1663 return
15171664
1518 def _on_transaction_progress_changed(self, backend, pkgname, progress):1665 def _on_transaction_progress_changed(self, backend, pkgname, progress):
1519 if self.app_details and self.app_details.pkgname and self.app_details.pkgname == pkgname:1666 if (self.app_details and
1667 self.app_details.pkgname and
1668 self.app_details.pkgname == pkgname):
1520 if not self.pkg_statusbar.progress.get_property('visible'):1669 if not self.pkg_statusbar.progress.get_property('visible'):
1521 self.pkg_statusbar.button.hide()1670 self.pkg_statusbar.button.hide()
1522 self.pkg_statusbar.progress.show()1671 self.pkg_statusbar.progress.show()
@@ -1528,8 +1677,8 @@
1528 return1677 return
15291678
1530 def get_app_icon_details(self):1679 def get_app_icon_details(self):
1531 """ helper for unity dbus support to provide details about the application1680 """ helper for unity dbus support to provide details about the
1532 icon as it is displayed on-screen1681 application icon as it is displayed on-screen
1533 """1682 """
1534 icon_size = self._get_app_icon_size_on_screen()1683 icon_size = self._get_app_icon_size_on_screen()
1535 (icon_x, icon_y) = self._get_app_icon_xy_position_on_screen()1684 (icon_x, icon_y) = self._get_app_icon_xy_position_on_screen()
@@ -1562,7 +1711,8 @@
1562 try:1711 try:
1563 (x,y) = self.icon.translate_coordinates(parent, 0, 0)1712 (x,y) = self.icon.translate_coordinates(parent, 0, 0)
1564 except Exception as e:1713 except Exception as e:
1565 LOG.warning("couldn't translate icon coordinates on-screen for unity dbus message: %s" % e)1714 LOG.warning("couldn't translate icon coordinates on-screen "
1715 "for unity dbus message: %s" % e)
1566 return (0,0)1716 return (0,0)
1567 # get toplevel window position1717 # get toplevel window position
1568 (px, py) = parent.get_position()1718 (px, py) = parent.get_position()
@@ -1575,14 +1725,17 @@
1575 return self.icons.load_icon(app_details.icon,1725 return self.icons.load_icon(app_details.icon,
1576 self.APP_ICON_SIZE, 0)1726 self.APP_ICON_SIZE, 0)
1577 except GObject.GError as e:1727 except GObject.GError as e:
1578 logging.warn("failed to load '%s': %s" % (app_details.icon, e))1728 logging.warn("failed to load '%s': %s" % (
1729 app_details.icon, e))
1579 return self.icons.load_icon(Icons.MISSING_APP,1730 return self.icons.load_icon(Icons.MISSING_APP,
1580 self.APP_ICON_SIZE, 0)1731 self.APP_ICON_SIZE, 0)
1581 elif app_details.icon_url:1732 elif app_details.icon_url:
1582 LOG.debug("did not find the icon locally, must download it")1733 LOG.debug("did not find the icon locally, must download it")
15831734
1584 def on_image_download_complete(downloader, image_file_path):1735 def on_image_download_complete(downloader, image_file_path):
1585 # when the download is complete, replace the icon in the view with the downloaded one1736 # when the download is complete, replace the icon in the
1737 # view with the downloaded one
1738 logging.debug("_get_icon_as_pixbuf:image_downloaded() %s" % image_file_path)
1586 try:1739 try:
1587 pb = GdkPixbuf.Pixbuf.new_from_file(image_file_path)1740 pb = GdkPixbuf.Pixbuf.new_from_file(image_file_path)
1588 self.icon.set_from_pixbuf(pb)1741 self.icon.set_from_pixbuf(pb)
@@ -1694,7 +1847,7 @@
1694 # gui1847 # gui
1695 win = Gtk.Window()1848 win = Gtk.Window()
1696 scroll = Gtk.ScrolledWindow()1849 scroll = Gtk.ScrolledWindow()
1697 view = AppDetailsViewGtk(db, distro, icons, cache, datadir, win)1850 view = AppDetailsView(db, distro, icons, cache, datadir, win)
16981851
1699 import sys1852 import sys
1700 if len(sys.argv) > 1:1853 if len(sys.argv) > 1:
17011854
=== added file 'test/gen-coverage-for-single-test.py'
--- test/gen-coverage-for-single-test.py 1970-01-01 00:00:00 +0000
+++ test/gen-coverage-for-single-test.py 2011-11-29 19:57:26 +0000
@@ -0,0 +1,4 @@
1#!/bin/sh
2
3python-coverage run --parallel $1
4./gen-coverage-report.sh
05
=== modified file 'test/gtk3/test_appdetailsview.py'
--- test/gtk3/test_appdetailsview.py 2011-11-11 03:20:16 +0000
+++ test/gtk3/test_appdetailsview.py 2011-11-29 19:57:26 +0000
@@ -1,21 +1,26 @@
1#!/usr/bin/python1#!/usr/bin/python
22
3from gi.repository import Gtk3from gi.repository import Gtk
4
5import os
4import sys6import sys
7import time
5import unittest8import unittest
69
7sys.path.insert(0,"../..")10sys.path.insert(0,"../..")
8sys.path.insert(0,"..")11sys.path.insert(0,"..")
912
10#from mock import Mock13from mock import Mock
1114
12import softwarecenter.paths15import softwarecenter.paths
13softwarecenter.paths.datadir = "../data"16softwarecenter.paths.datadir = "../data"
1417
15from softwarecenter.db.application import Application18from softwarecenter.db.application import Application
16from softwarecenter.testutils import get_mock_app_from_real_app19from softwarecenter.testutils import get_mock_app_from_real_app, do_events
17from softwarecenter.ui.gtk3.views.appdetailsview_gtk import get_test_window_appdetails20from softwarecenter.ui.gtk3.views.appdetailsview import get_test_window_appdetails
18class TestAppdetailsViews(unittest.TestCase):21from softwarecenter.enums import PkgStates
22
23class TestAppdetailsView(unittest.TestCase):
1924
20 def test_videoplayer(self):25 def test_videoplayer(self):
21 # get the widget26 # get the widget
@@ -25,8 +30,7 @@
25 # show app with no video30 # show app with no video
26 app = Application("", "2vcard")31 app = Application("", "2vcard")
27 view.show_app(app)32 view.show_app(app)
28 while Gtk.events_pending():33 do_events()
29 Gtk.main_iteration()
30 self.assertFalse(view.videoplayer.get_property("visible"))34 self.assertFalse(view.videoplayer.get_property("visible"))
3135
32 # create app with video and ensure its visible36 # create app with video and ensure its visible
@@ -36,9 +40,115 @@
36 # this is a example html - any html5 video will do40 # this is a example html - any html5 video will do
37 details.video_url = "http://people.canonical.com/~mvo/totem.html"41 details.video_url = "http://people.canonical.com/~mvo/totem.html"
38 view.show_app(mock)42 view.show_app(mock)
39 while Gtk.events_pending():43 do_events()
40 Gtk.main_iteration()
41 self.assertTrue(view.videoplayer.get_property("visible"))44 self.assertTrue(view.videoplayer.get_property("visible"))
45
46 def test_page(self):
47 win = get_test_window_appdetails()
48 view = win.get_data("view")
49 # show app
50 app = Application("", "software-center")
51 view.show_app(app)
52 do_events()
53
54 # create mock app
55 mock_app = get_mock_app_from_real_app(app)
56 view.app = mock_app
57 mock_details = mock_app.get_details(None)
58 mock_details.purchase_date = "2011-11-20 17:45:01"
59 mock_details._error_not_found = "error not found"
60 view.app_details = mock_details
61
62 # show a app through the various states
63 for var in vars(PkgStates):
64 # FIXME: this just ensures we are not crashing, also
65 # add functional tests to ensure on error we show
66 # the right info etc
67 state = getattr(PkgStates, var)
68 mock_details.pkg_state = state
69 # reset app to ensure its shown again
70 view.app = None
71 # show it
72 view.show_app(mock_app)
73
74 def test_app_icon_loading(self):
75 win = get_test_window_appdetails()
76 view = win.get_data("view")
77 # get icon
78 app = Application("", "software-center")
79 mock_app = get_mock_app_from_real_app(app)
80 view.app = mock_app
81 mock_details = mock_app.get_details(None)
82 mock_details.cached_icon_file_path = "download-icon-test"
83 mock_details.icon = "favicon.ico"
84 mock_details.icon_url = "http://de.wikipedia.org/favicon.ico"
85 view.app_details = mock_details
86 view.show_app(mock_app)
87 do_events()
88 # ensure the icon is there
89 # FIXME: ensure that the icon is really downloaded
90 self.assertTrue(os.path.exists(mock_details.cached_icon_file_path))
91 os.unlink(mock_details.cached_icon_file_path)
92
93 def test_add_where_is_it(self):
94 win = get_test_window_appdetails()
95 view = win.get_data("view")
96 app = Application("", "software-center")
97 view.show_app(app)
98 view._add_where_is_it_commandline("apt")
99 do_events()
100 view._add_where_is_it_launcher("/usr/share/applications/ubuntu-software-center.desktop")
101 do_events()
102
103 def test_pkgstatus_bar(self):
104 # make sure configure is run with the various states
105 # test
106 win = get_test_window_appdetails()
107 view = win.get_data("view")
108 # show app
109 app = Application("", "software-center")
110 view.show_app(app)
111 do_events()
112
113 # create mock app
114 mock_app = get_mock_app_from_real_app(app)
115 view.app = mock_app
116 mock_details = mock_app.get_details(None)
117 mock_details.purchase_date = "2011-11-20 17:45:01"
118 view.app_details = mock_details
119
120 # run the configure on the various states for the pkgstatus bar
121 for var in vars(PkgStates):
122 # FIXME: this just ensures we are not crashing, also
123 # add functional tests to ensure on error we show
124 # the right info etc
125 state = getattr(PkgStates, var)
126 mock_details.pkg_state = state
127 # FIXME2: we should make configure simpler and/or explain
128 # why it gets the state instead of just reading it
129 # from the app_details
130 view.pkg_statusbar.configure(mock_details, state)
131
132 # make sure the various states are tested for click
133 view.pkg_statusbar.app_manager = mock = Mock()
134 mock_button = Mock()
135 button_to_function_tests = (
136 (PkgStates.INSTALLED, "remove"),
137 (PkgStates.PURCHASED_BUT_REPO_MUST_BE_ENABLED, "reinstall_purchased"),
138 (PkgStates.NEEDS_PURCHASE, "buy_app"),
139 (PkgStates.UNINSTALLED, "install"),
140 (PkgStates.REINSTALLABLE, "install"),
141 (PkgStates.UPGRADABLE, "upgrade"),
142 (PkgStates.NEEDS_SOURCE, "enable_software_source")
143 )
144 for state, func in button_to_function_tests:
145 view.pkg_statusbar.pkg_state = state
146 view.pkg_statusbar._on_button_clicked(mock_button)
147 self.assertTrue(
148 getattr(mock, func).called,
149 "for state %s the function %s was not called" % (state, func))
150 mock.reset()
151
42152
43if __name__ == "__main__":153if __name__ == "__main__":
44 import logging154 import logging
45155
=== modified file 'test/gtk3/test_install_progress.py'
--- test/gtk3/test_install_progress.py 2011-08-17 08:21:22 +0000
+++ test/gtk3/test_install_progress.py 2011-11-29 19:57:26 +0000
@@ -27,7 +27,7 @@
27 stop_dummy_backend()27 stop_dummy_backend()
2828
29 def test_install_appdetails(self):29 def test_install_appdetails(self):
30 from softwarecenter.ui.gtk3.views.appdetailsview_gtk import get_test_window_appdetails30 from softwarecenter.ui.gtk3.views.appdetailsview import get_test_window_appdetails
31 win = get_test_window_appdetails()31 win = get_test_window_appdetails()
32 view = win.get_data("view")32 view = win.get_data("view")
33 view.show_app(Application("", "2vcard"))33 view.show_app(Application("", "2vcard"))
3434
=== modified file 'test/gtk3/test_views.py'
--- test/gtk3/test_views.py 2011-08-24 09:39:59 +0000
+++ test/gtk3/test_views.py 2011-11-29 19:57:26 +0000
@@ -29,7 +29,7 @@
29 Gtk.main()29 Gtk.main()
3030
31 def test_appdetails(self):31 def test_appdetails(self):
32 from softwarecenter.ui.gtk3.views.appdetailsview_gtk import get_test_window_appdetails32 from softwarecenter.ui.gtk3.views.appdetailsview import get_test_window_appdetails
33 win = get_test_window_appdetails()33 win = get_test_window_appdetails()
34 GObject.timeout_add(TIMEOUT, lambda: win.destroy())34 GObject.timeout_add(TIMEOUT, lambda: win.destroy())
35 Gtk.main()35 Gtk.main()

Subscribers

People subscribed via source and target branches