Merge lp:~mvo/software-center/dataprovider-desktop-dependency-property into lp:software-center

Proposed by Michael Vogt
Status: Merged
Merged at revision: 3185
Proposed branch: lp:~mvo/software-center/dataprovider-desktop-dependency-property
Merge into: lp:software-center
Diff against target: 246 lines (+119/-19)
3 files modified
softwarecenter/db/application.py (+19/-3)
softwarecenter/db/dataprovider.py (+18/-14)
tests/test_dataprovider.py (+82/-2)
To merge this branch: bzr merge lp:~mvo/software-center/dataprovider-desktop-dependency-property
Reviewer Review Type Date Requested Status
Michael Nelson Approve
Review via email: mp+124462@code.launchpad.net

Description of the change

Small branch for the unity guys

To post a comment you must log in.
Revision history for this message
Michael Vogt (mvo) wrote :

Setting back to WIP until I get a +1 from the unity people

Revision history for this message
Michael Vogt (mvo) wrote :

According to pstolowski it crashes with:

(process:24210): unity-applications-daemon-WARNING **: daemon.vala:1263: Failed to get package details for 'application://keepassx.desktop': GDBus.Error:org.freedesktop.DBus.Python.TypeError: Traceback (most recent call last):
  File "/usr/lib/python2.7/dist-packages/dbus/service.py", line 751, in _message_cb
    _method_reply_return(connection, message, method_name, signature, *retval)
  File "/usr/lib/python2.7/dist-packages/dbus/service.py", line 254, in _method_reply_return
    reply.append(signature=signature, *retval)
TypeError: Don't know which D-Bus type to use to encode type "set"

so definitely a issue with the test not catching this.

Revision history for this message
Michael Vogt (mvo) wrote :

Setting to needs-review based on:
"""
From: Pawel Stolowski
Hi Michael,

Your branch now works fine, thanks a lot for your work!
"""

Revision history for this message
Michael Nelson (michael.nelson) wrote :

06:47 < mvo> I wonder if I could get a quick review for https://code.launchpad.net/~mvo/software-center/dataprovider-desktop-dependency-property/+merge/124462 to merge/upload it in before the b2 freeze (which will probably start around noon today)
06:47 * noodles looks
06:49 < noodles> mvo: why do you need lines 17-19 when you've got lines 23?
06:49 < noodles> mvo: ah, nm.
06:49 < noodles> I see (I'd reversed the removed/added)
06:52 < noodles> mvo: looks great. I'm assuming you didn't mean to leave 183-184 in the diff? But whatever - see what you think.
06:52 * noodles adds +1.
06:53 < noodles> mvo: also, it'd be pretty easy to update the two tests that you've already touched there to actually test values when there's a set, or empty list or empty dict? Just a thought.

review: Approve
3179. By Michael Vogt

improve tests to actually cover AppDetails.as_dbus_property_dict() explicitely

3180. By Michael Vogt

trivial docstring updates

3181. By Michael Vogt

add real dbus bus test

Preview Diff

[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
=== modified file 'softwarecenter/db/application.py'
--- softwarecenter/db/application.py 2012-09-14 09:19:09 +0000
+++ softwarecenter/db/application.py 2012-09-18 08:18:20 +0000
@@ -261,7 +261,7 @@
261 raise ValueError("pkg '%s' has not archive_suite '%s'" % (261 raise ValueError("pkg '%s' has not archive_suite '%s'" % (
262 pkg, archive_suite))262 pkg, archive_suite))
263263
264 def as_property_dict(self):264 def as_dbus_property_dict(self):
265 """ return all properties as a dict with name/value """265 """ return all properties as a dict with name/value """
266 import inspect266 import inspect
267 properties = {}267 properties = {}
@@ -270,12 +270,19 @@
270 for name, value in inspect.getmembers(self):270 for name, value in inspect.getmembers(self):
271 if name.startswith("_") or callable(value) or name in NOT_EXPOSE:271 if name.startswith("_") or callable(value) or name in NOT_EXPOSE:
272 continue272 continue
273 # dbus does not like sets
274 if isinstance(value, set):
275 value = list(value)
273 # make sure that the type can be encoded276 # make sure that the type can be encoded
274 if not type(value) in [277 if not type(value) in [
275 list, dict, tuple, set, basestring, int, float, bool, None]:278 list, dict, tuple, basestring, int, float, bool, None]:
276 value = "%s" % value279 value = "%s" % value
280 # dbus does not like empty dicts/lists
281 if isinstance(value, dict) or isinstance(value, list):
282 if len(value) == 0:
283 value = ""
277 # and set it284 # and set it
278 properties[name] = value or ""285 properties[name] = value
279 return properties286 return properties
280287
281 @property288 @property
@@ -408,6 +415,15 @@
408 return self._history.get_installed_date(self.pkgname)415 return self._history.get_installed_date(self.pkgname)
409416
410 @property417 @property
418 def is_desktop_dependency(self):
419 if self._pkg:
420 depends = self._cache.get_packages_removed_on_remove(
421 self._pkg)
422 if "ubuntu-desktop" in depends:
423 return True
424 return False
425
426 @property
411 def purchase_date(self):427 def purchase_date(self):
412 if self._doc:428 if self._doc:
413 return self._doc.get_value(XapianValues.PURCHASED_DATE)429 return self._doc.get_value(XapianValues.PURCHASED_DATE)
414430
=== modified file 'softwarecenter/db/dataprovider.py'
--- softwarecenter/db/dataprovider.py 2012-09-14 07:12:33 +0000
+++ softwarecenter/db/dataprovider.py 2012-09-18 08:18:20 +0000
@@ -35,20 +35,24 @@
35from softwarecenter.db.utils import run_software_center_agent35from softwarecenter.db.utils import run_software_center_agent
3636
37# To test, run with e.g.37# To test, run with e.g.
38# dbus-send --session --type=method_call \38"""
39# --dest=com.ubuntu.SoftwareCenterDataProvider --print-reply \39dbus-send --session --type=method_call \
40# /com/ubuntu/SoftwareCenterDataProvider40 --dest=com.ubuntu.SoftwareCenterDataProvider --print-reply \
41# com.ubuntu.SoftwareCenterDataProvider.GetAppDetails string:"" string:"apt"41 /com/ubuntu/SoftwareCenterDataProvider \
42#42 com.ubuntu.SoftwareCenterDataProvider.GetAppDetails string:"" string:"apt"
43"""
4344
4445
45LOG = logging.getLogger(__file__)46LOG = logging.getLogger(__file__)
4647
48DBUS_BUS_NAME = 'com.ubuntu.SoftwareCenterDataProvider'
49DBUS_DATA_PROVIDER_IFACE = 'com.ubuntu.SoftwareCenterDataProvider'
50DBUS_DATA_PROVIDER_PATH = '/com/ubuntu/SoftwareCenterDataProvider'
51
4752
48class SoftwareCenterDataProvider(dbus.service.Object):53class SoftwareCenterDataProvider(dbus.service.Object):
4954
50 def __init__(self, bus_name,55 def __init__(self, bus_name, object_path=DBUS_DATA_PROVIDER_PATH):
51 object_path='/com/ubuntu/SoftwareCenterDataProvider'):
52 dbus.service.Object.__init__(self, bus_name, object_path)56 dbus.service.Object.__init__(self, bus_name, object_path)
53 self.bus_name = bus_name57 self.bus_name = bus_name
54 # the database58 # the database
@@ -67,14 +71,14 @@
67 """ stop the dbus controller and remove from the bus """71 """ stop the dbus controller and remove from the bus """
68 self.remove_from_connection()72 self.remove_from_connection()
6973
70 @dbus.service.method('com.ubuntu.SoftwareCenterDataProvider',74 @dbus.service.method(DBUS_DATA_PROVIDER_IFACE,
71 in_signature='ss', out_signature='a{sv}')75 in_signature='ss', out_signature='a{sv}')
72 def GetAppDetails(self, appname, pkgname):76 def GetAppDetails(self, appname, pkgname):
73 LOG.debug("GetAppDetails() called with ('%s', '%s')" % (77 LOG.debug("GetAppDetails() called with ('%s', '%s')" % (
74 appname, pkgname))78 appname, pkgname))
75 app = Application(appname, pkgname)79 app = Application(appname, pkgname)
76 appdetails = app.get_details(self.db)80 appdetails = app.get_details(self.db)
77 return appdetails.as_property_dict()81 return appdetails.as_dbus_property_dict()
7882
79 @dbus.service.method('com.ubuntu.SoftwareCenterDataProvider',83 @dbus.service.method('com.ubuntu.SoftwareCenterDataProvider',
80 in_signature='', out_signature='as')84 in_signature='', out_signature='as')
@@ -82,7 +86,7 @@
82 LOG.debug("GetAvailableCategories() called")86 LOG.debug("GetAvailableCategories() called")
83 return [cat.name for cat in self.categories]87 return [cat.name for cat in self.categories]
8488
85 @dbus.service.method('com.ubuntu.SoftwareCenterDataProvider',89 @dbus.service.method(DBUS_DATA_PROVIDER_IFACE,
86 in_signature='s', out_signature='as')90 in_signature='s', out_signature='as')
87 def GetAvailableSubcategories(self, category_name):91 def GetAvailableSubcategories(self, category_name):
88 LOG.debug("GetAvailableSubcategories() called")92 LOG.debug("GetAvailableSubcategories() called")
@@ -105,10 +109,10 @@
105 return result109 return result
106110
107111
108def dbus_main():112def dbus_main(bus=None):
109 bus = dbus.SessionBus()113 if bus is None:
110 bus_name = dbus.service.BusName(114 bus = dbus.SessionBus()
111 'com.ubuntu.SoftwareCenterDataProvider', bus)115 bus_name = dbus.service.BusName(DBUS_BUS_NAME, bus)
112 data_provider = SoftwareCenterDataProvider(bus_name)116 data_provider = SoftwareCenterDataProvider(bus_name)
113 data_provider # pyflakes117 data_provider # pyflakes
114118
115119
=== modified file 'tests/test_dataprovider.py'
--- tests/test_dataprovider.py 2012-09-12 12:41:05 +0000
+++ tests/test_dataprovider.py 2012-09-18 08:18:20 +0000
@@ -1,7 +1,14 @@
11
2import dbus2import dbus
3import time
3import unittest4import unittest
45
6from dbus.mainloop.glib import DBusGMainLoop
7DBusGMainLoop(set_as_default=True)
8
9from multiprocessing import Process
10from mock import Mock
11
5from tests.utils import (12from tests.utils import (
6 kill_process,13 kill_process,
7 setup_test_env,14 setup_test_env,
@@ -9,11 +16,76 @@
9)16)
10setup_test_env()17setup_test_env()
1118
12from softwarecenter.db.dataprovider import SoftwareCenterDataProvider19from softwarecenter.db.application import AppDetails
1320from softwarecenter.db.dataprovider import (
21 SoftwareCenterDataProvider,
22 dbus_main,
23 DBUS_BUS_NAME,
24 DBUS_DATA_PROVIDER_IFACE,
25 DBUS_DATA_PROVIDER_PATH,
26 )
27
28
29class DbusForRealTestCase(unittest.TestCase):
30 """Test the dataprovider over a real dbus bus"""
31
32 @classmethod
33 def setUpClass(cls):
34 cls.p = Process(target=dbus_main)
35 cls.p.start()
36 time.sleep(1)
37
38 @classmethod
39 def tearDown(cls):
40 cls.p.terminate()
41
42 def setUp(self):
43 bus = dbus.SessionBus()
44 obj = bus.get_object(bus_name=DBUS_BUS_NAME,
45 object_path=DBUS_DATA_PROVIDER_PATH,
46 follow_name_owner_changes=True)
47 self.proxy = dbus.Interface(object=obj,
48 dbus_interface=DBUS_DATA_PROVIDER_IFACE)
49
50 def test_dbus_for_real(self):
51 result = self.proxy.GetAppDetails("", "gedit")
52 self.assertEqual(result["pkgname"], "gedit")
53
54
55class PropertyDictExceptionsTestCase(unittest.TestCase):
56 """Test that the exceptions in the AppDetails for the dbus properties
57 are handled correctly
58 """
59
60 def setUp(self):
61 self.mock_app_details = Mock(AppDetails)
62
63 def test_simple(self):
64 self.mock_app_details.name = "fake-app"
65 properties = AppDetails.as_dbus_property_dict(self.mock_app_details)
66 self.assertEqual(properties["name"], "fake-app")
67
68 def test_empty_dict_set(self):
69 self.mock_app_details.empty_set = set()
70 self.mock_app_details.empty_dict = {}
71 properties = AppDetails.as_dbus_property_dict(self.mock_app_details)
72 self.assertEqual(properties["empty_set"], "")
73 self.assertEqual(properties["empty_dict"], "")
74
75 def test_normal_dict(self):
76 self.mock_app_details.non_empty_dict = { "moo" : "bar" }
77 properties = AppDetails.as_dbus_property_dict(self.mock_app_details)
78 self.assertEqual(properties["non_empty_dict"], { "moo" : "bar" })
79
80 def test_normal_set(self):
81 self.mock_app_details.non_empty_set = set(["foo", "bar", "baz"])
82 properties = AppDetails.as_dbus_property_dict(self.mock_app_details)
83 self.assertEqual(
84 sorted(properties["non_empty_set"]), ["bar", "baz", "foo"])
1485
1586
16class DataProviderTestCase(unittest.TestCase):87class DataProviderTestCase(unittest.TestCase):
88 """ Test the methods of the dataprovider """
1789
18 @classmethod90 @classmethod
19 def setUpClass(cls):91 def setUpClass(cls):
@@ -41,6 +113,11 @@
41 self.assertEqual(result["pkgname"], "gedit")113 self.assertEqual(result["pkgname"], "gedit")
42 self.assertEqual(result["price"], "Free")114 self.assertEqual(result["price"], "Free")
43 self.assertEqual(result["raw_price"], "")115 self.assertEqual(result["raw_price"], "")
116 self.assertEqual(result["is_desktop_dependency"], True)
117
118 def test_get_details_non_desktop(self):
119 result = self.provider.GetAppDetails("", "apache2")
120 self.assertEqual(result["is_desktop_dependency"], False)
44121
45 # get available categories/subcategories122 # get available categories/subcategories
46 def test_get_categories(self):123 def test_get_categories(self):
@@ -69,5 +146,8 @@
69 result = self.provider.GetItemsForCategory(u"What\u2019s New")146 result = self.provider.GetItemsForCategory(u"What\u2019s New")
70 self.assertEqual(len(result), 20)147 self.assertEqual(len(result), 20)
71148
149
72if __name__ == "__main__":150if __name__ == "__main__":
151 #import logging
152 #logging.basicConfig(level=logging.DEBUG)
73 unittest.main()153 unittest.main()

Subscribers

People subscribed via source and target branches