Merge lp:~mvo/software-center/dataprovider-desktop-dependency-property into lp:software-center
- dataprovider-desktop-dependency-property
- Merge into trunk
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 |
Related bugs: |
Reviewer | Review Type | Date Requested | Status |
---|---|---|---|
Michael Nelson | Approve | ||
Review via email: mp+124462@code.launchpad.net |
Commit message
Description of the change
Small branch for the unity guys
Michael Vogt (mvo) wrote : | # |
Michael Vogt (mvo) wrote : | # |
According to pstolowski it crashes with:
(process:24210): unity-applicati
File "/usr/lib/
_method_
File "/usr/lib/
reply.
TypeError: Don't know which D-Bus type to use to encode type "set"
so definitely a issue with the test not catching this.
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!
"""
Michael Nelson (michael.nelson) wrote : | # |
06:47 < mvo> I wonder if I could get a quick review for https:/
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.
- 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
1 | === modified file 'softwarecenter/db/application.py' | |||
2 | --- softwarecenter/db/application.py 2012-09-14 09:19:09 +0000 | |||
3 | +++ softwarecenter/db/application.py 2012-09-18 08:18:20 +0000 | |||
4 | @@ -261,7 +261,7 @@ | |||
5 | 261 | raise ValueError("pkg '%s' has not archive_suite '%s'" % ( | 261 | raise ValueError("pkg '%s' has not archive_suite '%s'" % ( |
6 | 262 | pkg, archive_suite)) | 262 | pkg, archive_suite)) |
7 | 263 | 263 | ||
9 | 264 | def as_property_dict(self): | 264 | def as_dbus_property_dict(self): |
10 | 265 | """ return all properties as a dict with name/value """ | 265 | """ return all properties as a dict with name/value """ |
11 | 266 | import inspect | 266 | import inspect |
12 | 267 | properties = {} | 267 | properties = {} |
13 | @@ -270,12 +270,19 @@ | |||
14 | 270 | for name, value in inspect.getmembers(self): | 270 | for name, value in inspect.getmembers(self): |
15 | 271 | if name.startswith("_") or callable(value) or name in NOT_EXPOSE: | 271 | if name.startswith("_") or callable(value) or name in NOT_EXPOSE: |
16 | 272 | continue | 272 | continue |
17 | 273 | # dbus does not like sets | ||
18 | 274 | if isinstance(value, set): | ||
19 | 275 | value = list(value) | ||
20 | 273 | # make sure that the type can be encoded | 276 | # make sure that the type can be encoded |
21 | 274 | if not type(value) in [ | 277 | if not type(value) in [ |
23 | 275 | list, dict, tuple, set, basestring, int, float, bool, None]: | 278 | list, dict, tuple, basestring, int, float, bool, None]: |
24 | 276 | value = "%s" % value | 279 | value = "%s" % value |
25 | 280 | # dbus does not like empty dicts/lists | ||
26 | 281 | if isinstance(value, dict) or isinstance(value, list): | ||
27 | 282 | if len(value) == 0: | ||
28 | 283 | value = "" | ||
29 | 277 | # and set it | 284 | # and set it |
31 | 278 | properties[name] = value or "" | 285 | properties[name] = value |
32 | 279 | return properties | 286 | return properties |
33 | 280 | 287 | ||
34 | 281 | @property | 288 | @property |
35 | @@ -408,6 +415,15 @@ | |||
36 | 408 | return self._history.get_installed_date(self.pkgname) | 415 | return self._history.get_installed_date(self.pkgname) |
37 | 409 | 416 | ||
38 | 410 | @property | 417 | @property |
39 | 418 | def is_desktop_dependency(self): | ||
40 | 419 | if self._pkg: | ||
41 | 420 | depends = self._cache.get_packages_removed_on_remove( | ||
42 | 421 | self._pkg) | ||
43 | 422 | if "ubuntu-desktop" in depends: | ||
44 | 423 | return True | ||
45 | 424 | return False | ||
46 | 425 | |||
47 | 426 | @property | ||
48 | 411 | def purchase_date(self): | 427 | def purchase_date(self): |
49 | 412 | if self._doc: | 428 | if self._doc: |
50 | 413 | return self._doc.get_value(XapianValues.PURCHASED_DATE) | 429 | return self._doc.get_value(XapianValues.PURCHASED_DATE) |
51 | 414 | 430 | ||
52 | === modified file 'softwarecenter/db/dataprovider.py' | |||
53 | --- softwarecenter/db/dataprovider.py 2012-09-14 07:12:33 +0000 | |||
54 | +++ softwarecenter/db/dataprovider.py 2012-09-18 08:18:20 +0000 | |||
55 | @@ -35,20 +35,24 @@ | |||
56 | 35 | from softwarecenter.db.utils import run_software_center_agent | 35 | from softwarecenter.db.utils import run_software_center_agent |
57 | 36 | 36 | ||
58 | 37 | # To test, run with e.g. | 37 | # To test, run with e.g. |
64 | 38 | # dbus-send --session --type=method_call \ | 38 | """ |
65 | 39 | # --dest=com.ubuntu.SoftwareCenterDataProvider --print-reply \ | 39 | dbus-send --session --type=method_call \ |
66 | 40 | # /com/ubuntu/SoftwareCenterDataProvider | 40 | --dest=com.ubuntu.SoftwareCenterDataProvider --print-reply \ |
67 | 41 | # com.ubuntu.SoftwareCenterDataProvider.GetAppDetails string:"" string:"apt" | 41 | /com/ubuntu/SoftwareCenterDataProvider \ |
68 | 42 | # | 42 | com.ubuntu.SoftwareCenterDataProvider.GetAppDetails string:"" string:"apt" |
69 | 43 | """ | ||
70 | 43 | 44 | ||
71 | 44 | 45 | ||
72 | 45 | LOG = logging.getLogger(__file__) | 46 | LOG = logging.getLogger(__file__) |
73 | 46 | 47 | ||
74 | 48 | DBUS_BUS_NAME = 'com.ubuntu.SoftwareCenterDataProvider' | ||
75 | 49 | DBUS_DATA_PROVIDER_IFACE = 'com.ubuntu.SoftwareCenterDataProvider' | ||
76 | 50 | DBUS_DATA_PROVIDER_PATH = '/com/ubuntu/SoftwareCenterDataProvider' | ||
77 | 51 | |||
78 | 47 | 52 | ||
79 | 48 | class SoftwareCenterDataProvider(dbus.service.Object): | 53 | class SoftwareCenterDataProvider(dbus.service.Object): |
80 | 49 | 54 | ||
83 | 50 | def __init__(self, bus_name, | 55 | def __init__(self, bus_name, object_path=DBUS_DATA_PROVIDER_PATH): |
82 | 51 | object_path='/com/ubuntu/SoftwareCenterDataProvider'): | ||
84 | 52 | dbus.service.Object.__init__(self, bus_name, object_path) | 56 | dbus.service.Object.__init__(self, bus_name, object_path) |
85 | 53 | self.bus_name = bus_name | 57 | self.bus_name = bus_name |
86 | 54 | # the database | 58 | # the database |
87 | @@ -67,14 +71,14 @@ | |||
88 | 67 | """ stop the dbus controller and remove from the bus """ | 71 | """ stop the dbus controller and remove from the bus """ |
89 | 68 | self.remove_from_connection() | 72 | self.remove_from_connection() |
90 | 69 | 73 | ||
92 | 70 | @dbus.service.method('com.ubuntu.SoftwareCenterDataProvider', | 74 | @dbus.service.method(DBUS_DATA_PROVIDER_IFACE, |
93 | 71 | in_signature='ss', out_signature='a{sv}') | 75 | in_signature='ss', out_signature='a{sv}') |
94 | 72 | def GetAppDetails(self, appname, pkgname): | 76 | def GetAppDetails(self, appname, pkgname): |
95 | 73 | LOG.debug("GetAppDetails() called with ('%s', '%s')" % ( | 77 | LOG.debug("GetAppDetails() called with ('%s', '%s')" % ( |
96 | 74 | appname, pkgname)) | 78 | appname, pkgname)) |
97 | 75 | app = Application(appname, pkgname) | 79 | app = Application(appname, pkgname) |
98 | 76 | appdetails = app.get_details(self.db) | 80 | appdetails = app.get_details(self.db) |
100 | 77 | return appdetails.as_property_dict() | 81 | return appdetails.as_dbus_property_dict() |
101 | 78 | 82 | ||
102 | 79 | @dbus.service.method('com.ubuntu.SoftwareCenterDataProvider', | 83 | @dbus.service.method('com.ubuntu.SoftwareCenterDataProvider', |
103 | 80 | in_signature='', out_signature='as') | 84 | in_signature='', out_signature='as') |
104 | @@ -82,7 +86,7 @@ | |||
105 | 82 | LOG.debug("GetAvailableCategories() called") | 86 | LOG.debug("GetAvailableCategories() called") |
106 | 83 | return [cat.name for cat in self.categories] | 87 | return [cat.name for cat in self.categories] |
107 | 84 | 88 | ||
109 | 85 | @dbus.service.method('com.ubuntu.SoftwareCenterDataProvider', | 89 | @dbus.service.method(DBUS_DATA_PROVIDER_IFACE, |
110 | 86 | in_signature='s', out_signature='as') | 90 | in_signature='s', out_signature='as') |
111 | 87 | def GetAvailableSubcategories(self, category_name): | 91 | def GetAvailableSubcategories(self, category_name): |
112 | 88 | LOG.debug("GetAvailableSubcategories() called") | 92 | LOG.debug("GetAvailableSubcategories() called") |
113 | @@ -105,10 +109,10 @@ | |||
114 | 105 | return result | 109 | return result |
115 | 106 | 110 | ||
116 | 107 | 111 | ||
121 | 108 | def dbus_main(): | 112 | def dbus_main(bus=None): |
122 | 109 | bus = dbus.SessionBus() | 113 | if bus is None: |
123 | 110 | bus_name = dbus.service.BusName( | 114 | bus = dbus.SessionBus() |
124 | 111 | 'com.ubuntu.SoftwareCenterDataProvider', bus) | 115 | bus_name = dbus.service.BusName(DBUS_BUS_NAME, bus) |
125 | 112 | data_provider = SoftwareCenterDataProvider(bus_name) | 116 | data_provider = SoftwareCenterDataProvider(bus_name) |
126 | 113 | data_provider # pyflakes | 117 | data_provider # pyflakes |
127 | 114 | 118 | ||
128 | 115 | 119 | ||
129 | === modified file 'tests/test_dataprovider.py' | |||
130 | --- tests/test_dataprovider.py 2012-09-12 12:41:05 +0000 | |||
131 | +++ tests/test_dataprovider.py 2012-09-18 08:18:20 +0000 | |||
132 | @@ -1,7 +1,14 @@ | |||
133 | 1 | 1 | ||
134 | 2 | import dbus | 2 | import dbus |
135 | 3 | import time | ||
136 | 3 | import unittest | 4 | import unittest |
137 | 4 | 5 | ||
138 | 6 | from dbus.mainloop.glib import DBusGMainLoop | ||
139 | 7 | DBusGMainLoop(set_as_default=True) | ||
140 | 8 | |||
141 | 9 | from multiprocessing import Process | ||
142 | 10 | from mock import Mock | ||
143 | 11 | |||
144 | 5 | from tests.utils import ( | 12 | from tests.utils import ( |
145 | 6 | kill_process, | 13 | kill_process, |
146 | 7 | setup_test_env, | 14 | setup_test_env, |
147 | @@ -9,11 +16,76 @@ | |||
148 | 9 | ) | 16 | ) |
149 | 10 | setup_test_env() | 17 | setup_test_env() |
150 | 11 | 18 | ||
153 | 12 | from softwarecenter.db.dataprovider import SoftwareCenterDataProvider | 19 | from softwarecenter.db.application import AppDetails |
154 | 13 | 20 | from softwarecenter.db.dataprovider import ( | |
155 | 21 | SoftwareCenterDataProvider, | ||
156 | 22 | dbus_main, | ||
157 | 23 | DBUS_BUS_NAME, | ||
158 | 24 | DBUS_DATA_PROVIDER_IFACE, | ||
159 | 25 | DBUS_DATA_PROVIDER_PATH, | ||
160 | 26 | ) | ||
161 | 27 | |||
162 | 28 | |||
163 | 29 | class DbusForRealTestCase(unittest.TestCase): | ||
164 | 30 | """Test the dataprovider over a real dbus bus""" | ||
165 | 31 | |||
166 | 32 | @classmethod | ||
167 | 33 | def setUpClass(cls): | ||
168 | 34 | cls.p = Process(target=dbus_main) | ||
169 | 35 | cls.p.start() | ||
170 | 36 | time.sleep(1) | ||
171 | 37 | |||
172 | 38 | @classmethod | ||
173 | 39 | def tearDown(cls): | ||
174 | 40 | cls.p.terminate() | ||
175 | 41 | |||
176 | 42 | def setUp(self): | ||
177 | 43 | bus = dbus.SessionBus() | ||
178 | 44 | obj = bus.get_object(bus_name=DBUS_BUS_NAME, | ||
179 | 45 | object_path=DBUS_DATA_PROVIDER_PATH, | ||
180 | 46 | follow_name_owner_changes=True) | ||
181 | 47 | self.proxy = dbus.Interface(object=obj, | ||
182 | 48 | dbus_interface=DBUS_DATA_PROVIDER_IFACE) | ||
183 | 49 | |||
184 | 50 | def test_dbus_for_real(self): | ||
185 | 51 | result = self.proxy.GetAppDetails("", "gedit") | ||
186 | 52 | self.assertEqual(result["pkgname"], "gedit") | ||
187 | 53 | |||
188 | 54 | |||
189 | 55 | class PropertyDictExceptionsTestCase(unittest.TestCase): | ||
190 | 56 | """Test that the exceptions in the AppDetails for the dbus properties | ||
191 | 57 | are handled correctly | ||
192 | 58 | """ | ||
193 | 59 | |||
194 | 60 | def setUp(self): | ||
195 | 61 | self.mock_app_details = Mock(AppDetails) | ||
196 | 62 | |||
197 | 63 | def test_simple(self): | ||
198 | 64 | self.mock_app_details.name = "fake-app" | ||
199 | 65 | properties = AppDetails.as_dbus_property_dict(self.mock_app_details) | ||
200 | 66 | self.assertEqual(properties["name"], "fake-app") | ||
201 | 67 | |||
202 | 68 | def test_empty_dict_set(self): | ||
203 | 69 | self.mock_app_details.empty_set = set() | ||
204 | 70 | self.mock_app_details.empty_dict = {} | ||
205 | 71 | properties = AppDetails.as_dbus_property_dict(self.mock_app_details) | ||
206 | 72 | self.assertEqual(properties["empty_set"], "") | ||
207 | 73 | self.assertEqual(properties["empty_dict"], "") | ||
208 | 74 | |||
209 | 75 | def test_normal_dict(self): | ||
210 | 76 | self.mock_app_details.non_empty_dict = { "moo" : "bar" } | ||
211 | 77 | properties = AppDetails.as_dbus_property_dict(self.mock_app_details) | ||
212 | 78 | self.assertEqual(properties["non_empty_dict"], { "moo" : "bar" }) | ||
213 | 79 | |||
214 | 80 | def test_normal_set(self): | ||
215 | 81 | self.mock_app_details.non_empty_set = set(["foo", "bar", "baz"]) | ||
216 | 82 | properties = AppDetails.as_dbus_property_dict(self.mock_app_details) | ||
217 | 83 | self.assertEqual( | ||
218 | 84 | sorted(properties["non_empty_set"]), ["bar", "baz", "foo"]) | ||
219 | 14 | 85 | ||
220 | 15 | 86 | ||
221 | 16 | class DataProviderTestCase(unittest.TestCase): | 87 | class DataProviderTestCase(unittest.TestCase): |
222 | 88 | """ Test the methods of the dataprovider """ | ||
223 | 17 | 89 | ||
224 | 18 | @classmethod | 90 | @classmethod |
225 | 19 | def setUpClass(cls): | 91 | def setUpClass(cls): |
226 | @@ -41,6 +113,11 @@ | |||
227 | 41 | self.assertEqual(result["pkgname"], "gedit") | 113 | self.assertEqual(result["pkgname"], "gedit") |
228 | 42 | self.assertEqual(result["price"], "Free") | 114 | self.assertEqual(result["price"], "Free") |
229 | 43 | self.assertEqual(result["raw_price"], "") | 115 | self.assertEqual(result["raw_price"], "") |
230 | 116 | self.assertEqual(result["is_desktop_dependency"], True) | ||
231 | 117 | |||
232 | 118 | def test_get_details_non_desktop(self): | ||
233 | 119 | result = self.provider.GetAppDetails("", "apache2") | ||
234 | 120 | self.assertEqual(result["is_desktop_dependency"], False) | ||
235 | 44 | 121 | ||
236 | 45 | # get available categories/subcategories | 122 | # get available categories/subcategories |
237 | 46 | def test_get_categories(self): | 123 | def test_get_categories(self): |
238 | @@ -69,5 +146,8 @@ | |||
239 | 69 | result = self.provider.GetItemsForCategory(u"What\u2019s New") | 146 | result = self.provider.GetItemsForCategory(u"What\u2019s New") |
240 | 70 | self.assertEqual(len(result), 20) | 147 | self.assertEqual(len(result), 20) |
241 | 71 | 148 | ||
242 | 149 | |||
243 | 72 | if __name__ == "__main__": | 150 | if __name__ == "__main__": |
244 | 151 | #import logging | ||
245 | 152 | #logging.basicConfig(level=logging.DEBUG) | ||
246 | 73 | unittest.main() | 153 | unittest.main() |
Setting back to WIP until I get a +1 from the unity people