Merge lp:~mandel/ubuntuone-windows-installer/uninstall-old-app into lp:ubuntuone-windows-installer

Proposed by Manuel de la Peña
Status: Merged
Approved by: Manuel de la Peña
Approved revision: 89
Merged at revision: 78
Proposed branch: lp:~mandel/ubuntuone-windows-installer/uninstall-old-app
Merge into: lp:ubuntuone-windows-installer
Prerequisite: lp:~mandel/ubuntuone-windows-installer/check-old-app
Diff against target: 326 lines (+194/-43)
3 files modified
ubuntuone_installer/gui/qt/utils/__init__.py (+2/-0)
ubuntuone_installer/gui/qt/utils/tests/test_windows.py (+131/-18)
ubuntuone_installer/gui/qt/utils/windows.py (+61/-25)
To merge this branch: bzr merge lp:~mandel/ubuntuone-windows-installer/uninstall-old-app
Reviewer Review Type Date Requested Status
Diego Sarmentero (community) Approve
Natalia Bidart (community) Approve
Review via email: mp+77225@code.launchpad.net

Commit message

Provides a function that can be used to uninstall the old beta of ubuntu one.

Description of the change

Provides a function that can be used to uninstall the old beta of ubuntu one.

To post a comment you must log in.
76. By Manuel de la Peña

Merged with parent.

77. By Manuel de la Peña

Re-merged with parent.

78. By Manuel de la Peña

Remerged with parent.

79. By Manuel de la Peña

Merged with parent.

80. By Manuel de la Peña

Merged with parent.

81. By Manuel de la Peña

Remerged with parent for lint issues.

82. By Manuel de la Peña

Fixed lint issues.

83. By Manuel de la Peña

Re-merged with parent and fixed conflicts.

84. By Manuel de la Peña

Fixed some lint issues.

85. By Manuel de la Peña

Merged with parent.

86. By Manuel de la Peña

Fixed lint issues and pep8 ones.

Revision history for this message
Natalia Bidart (nataliabidart) wrote :

The code in the existent is_old_beta_installed should reuse the newly added get_property_for_product, so we do not add code duplication. Likewise, tests should be refactored so there is no duplication there (I'm talking about _assert_get_product_property and _assert_is_old_beta_installed).

review: Needs Fixing
87. By Manuel de la Peña

Reuse the method used to get a property.

Revision history for this message
Natalia Bidart (nataliabidart) wrote :

(11:51:59 AM) nessita: mandel: I'm re-reviewing uninstall-old-app. I guess that all the comments inside is_old_beta_installed should be moved to get_property_for_product. And, for clarity sake, I would advice defining get_property_for_product before the is_old_beta_installed function...
(11:52:09 AM) nessita: mandel: can you please change that?

review: Needs Fixing
Revision history for this message
Natalia Bidart (nataliabidart) wrote :

Also, this docstring:

    """Retruns the value of a fiven property from a product."""

should be written as a command (Returm the value...). Same for:

"""Will remove the old beta from the users machine."""

should be """Remove the old beta from the user's machine.""".

88. By Manuel de la Peña

Fixed docstrings and move the funtion so that is more clear for other developers.

89. By Manuel de la Peña

Fixed pep8 issues.

Revision history for this message
Natalia Bidart (nataliabidart) wrote :

Looks good now!

review: Approve
Revision history for this message
Diego Sarmentero (diegosarmentero) wrote :

+1

review: Approve

Preview Diff

[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
1=== modified file 'ubuntuone_installer/gui/qt/utils/__init__.py'
2--- ubuntuone_installer/gui/qt/utils/__init__.py 2011-09-29 13:26:44 +0000
3+++ ubuntuone_installer/gui/qt/utils/__init__.py 2011-10-10 15:22:19 +0000
4@@ -31,6 +31,7 @@
5 is_old_beta_installed = windows.is_old_beta_installed
6 are_updates_present = windows.are_updates_present
7 perform_update = windows.perform_update
8+ uninstall_old_beta = windows.uninstall_old_beta
9 else:
10 from ubuntuone_installer.gui.qt.utils import linux
11 uninstall_application = linux.uninstall_application
12@@ -40,3 +41,4 @@
13 is_old_beta_installed = lambda: False
14 are_updates_present = lambda *args, **kwargs: False
15 perform_update = lambda *args, **kwargs: None
16+ uninstall_old_beta = lambda *args, **kwargs: None
17
18=== modified file 'ubuntuone_installer/gui/qt/utils/tests/test_windows.py'
19--- ubuntuone_installer/gui/qt/utils/tests/test_windows.py 2011-10-04 10:43:26 +0000
20+++ ubuntuone_installer/gui/qt/utils/tests/test_windows.py 2011-10-10 15:22:19 +0000
21@@ -1,6 +1,7 @@
22 # -*- coding: utf-8 -*-
23
24 # Authors: Diego Sarmentero <diego.sarmentero@canonical.com>
25+# Manuel de la Pena <manuel@canonical.com>
26 #
27 # Copyright 2011 Canonical Ltd.
28 #
29@@ -237,41 +238,153 @@
30
31 """Test the update from the old beta."""
32
33- def _assert_is_old_beta_installed(self, return_code, expected_result):
34+ def _assert_is_old_beta_installed(self, return_value, expected_result):
35 """Helper to test the is_old_beta_installed."""
36 # vars used to know that we did indeed call the msi lib correctly
37 called = {}
38
39- def fake_get_product_info(uid, property_name, property_buffer,
40- size):
41+ def fake_get_product_property(uid, property_name):
42 """Fake call to the msi lib."""
43 called['uid'] = uid
44- called['property'] = property_name
45- called['property_buffer'] = property_buffer
46- called['size'] = size
47- return return_code
48+ called['property_name'] = property_name
49+ return return_value
50
51- self.patch(utils.windows.ctypes.windll.msi, 'MsiGetProductInfoW',
52- fake_get_product_info)
53+ self.patch(utils.windows, 'get_property_for_product',
54+ fake_get_product_property)
55 self.assertEqual(utils.is_old_beta_installed(), expected_result)
56 # assert that we indeed called the ctypes with the correct data
57 self.assertEqual(utils.windows.OLD_BETA_UID_KEY,
58- called['uid'].value)
59+ called['uid'])
60 self.assertEqual(utils.windows.VERSION_STRING_PROPERTY,
61- called['property'])
62+ called['property_name'])
63
64 def test_is_old_beta_installed_true(self):
65 """Test the function used to know if it is installed."""
66- self._assert_is_old_beta_installed(utils.windows.ERROR_SUCCESS, True)
67-
68- def test_is_old_beta_installed_more_data(self):
69- """Test the function used to know if it is installed."""
70- self._assert_is_old_beta_installed(utils.windows.ERROR_MORE_DATA, True)
71+ self._assert_is_old_beta_installed('SomeString', True)
72
73 def test_is_old_beta_installed_false(self):
74 """Test the function used to know if it is installed."""
75- self._assert_is_old_beta_installed(utils.windows.ERROR_UNKNOWN_PRODUCT,
76- False)
77+ self._assert_is_old_beta_installed(None, False)
78+
79+ def _assert_get_product_property(self, uid, property_name, return_code,
80+ expected_result):
81+ """Generic test for getting the property."""
82+ # vars used to know that we did indeed call the msi lib correctly
83+ called = {}
84+
85+ def fake_get_product_info(uid, property_name, property_buffer, size):
86+ """Fake call to the msi lib."""
87+ called['called_uid_buffer'] = uid
88+ called['called_property_name'] = property_name
89+ if expected_result:
90+ property_buffer.value = expected_result
91+ called['called_property_buffer'] = expected_result
92+ called['called_size'] = size
93+ return return_code
94+
95+ self.patch(utils.windows.ctypes.windll.msi, 'MsiGetProductInfoW',
96+ fake_get_product_info)
97+ self.assertEqual(expected_result,
98+ utils.windows.get_property_for_product(uid,
99+ property_name))
100+ self.assertEqual(uid, called['called_uid_buffer'], uid)
101+ self.assertEqual(property_name, called['called_property_name'])
102+
103+ def test_get_product_property_present(self):
104+ """Test that we do indeed get the correct value."""
105+ uid = u'{som-uid}'
106+ property_name = u'VersionString'
107+ return_code = 0
108+ self._assert_get_product_property(uid, property_name, return_code,
109+ property_name)
110+
111+ def test_get_product_property_not_present(self):
112+ """Test that we do get None."""
113+ uid = u'{som-uid}'
114+ property_name = u'VersionString'
115+ return_code = 1212
116+ self._assert_get_product_property(uid, property_name, return_code,
117+ None)
118+
119+ def test_uninstall_present(self):
120+ """Test uninstalling when the msi is present."""
121+ called = {}
122+ path = 'path/to/msi'
123+
124+ def fake_get_property_for_product(uid, property_name, buf_size=None):
125+ """A fake method."""
126+ called['property_uid'] = uid
127+ called['property_name'] = property_name
128+ return path
129+
130+ def fake_msi_install_product(callable_fn, path, cmd):
131+ """A fake install function."""
132+ called['callable_fn'] = callable_fn
133+ called['install_path'] = path
134+ called['cmd'] = cmd
135+ return defer.succeed(0)
136+
137+ self.patch(utils.windows, 'get_property_for_product',
138+ fake_get_property_for_product)
139+ self.patch(utils.windows, 'deferToThread',
140+ fake_msi_install_product)
141+ utils.uninstall_old_beta()
142+ self.assertEqual(called['property_uid'],
143+ utils.windows.OLD_BETA_UID_KEY)
144+ self.assertEqual(called['property_name'], u'LocalPackage')
145+ self.assertEqual(utils.windows.ctypes.windll.msi.MsiInstallProductW,
146+ called['callable_fn'])
147+ self.assertEqual(called['install_path'], path)
148+ self.assertEqual(called['cmd'], u'REMOVE=ALL')
149+
150+ def test_uninstall_error(self):
151+ """Test uninstalling with an error."""
152+ called = {}
153+ path = 'path/to/somewhere'
154+
155+ def fake_get_property_for_product(uid, property_name, buf_size=None):
156+ """A fake method."""
157+ called['property_uid'] = uid
158+ called['property_name'] = property_name
159+ return path
160+
161+ def fake_msi_install_product(callable_fn, path, cmd):
162+ """A fake install function."""
163+ called['callable_fn'] = callable_fn
164+ called['install_path'] = path
165+ called['cmd'] = cmd
166+ return defer.succeed(1)
167+
168+ self.patch(utils.windows, 'get_property_for_product',
169+ fake_get_property_for_product)
170+ self.patch(utils.windows, 'deferToThread',
171+ fake_msi_install_product)
172+ self.assertFailure(utils.uninstall_old_beta(),
173+ utils.windows.MsiException)
174+ self.assertEqual(called['property_uid'],
175+ utils.windows.OLD_BETA_UID_KEY)
176+ self.assertEqual(called['property_name'], u'LocalPackage')
177+ self.assertEqual(utils.windows.ctypes.windll.msi.MsiInstallProductW,
178+ called['callable_fn'])
179+ self.assertEqual(called['install_path'], path)
180+ self.assertEqual(called['cmd'], u'REMOVE=ALL')
181+
182+ def test_uninstall_not_present(self):
183+ """Test uninstalling when the msi is missing."""
184+ called = {}
185+
186+ def fake_get_property_for_product(uid, property_name):
187+ """A fake method."""
188+ called['uid'] = uid
189+ called['property_name'] = property_name
190+ return None
191+
192+ self.patch(utils.windows, 'get_property_for_product',
193+ fake_get_property_for_product)
194+ self.assertFailure(utils.uninstall_old_beta(),
195+ utils.windows.MsiException)
196+ self.assertEqual(called['uid'], utils.windows.OLD_BETA_UID_KEY)
197+ self.assertEqual(called['property_name'], u'LocalPackage')
198
199
200 class FakeLogger(object):
201
202=== modified file 'ubuntuone_installer/gui/qt/utils/windows.py'
203--- ubuntuone_installer/gui/qt/utils/windows.py 2011-10-03 14:21:48 +0000
204+++ ubuntuone_installer/gui/qt/utils/windows.py 2011-10-10 15:22:19 +0000
205@@ -1,6 +1,7 @@
206 # -*- coding: utf-8 -*-
207
208 # Authors: Diego Sarmentero <diego.sarmentero@canonical.com>
209+# Manuel de la Pena <manuel@canonical.com>
210 #
211 # Copyright 2011 Canonical Ltd.
212 #
213@@ -34,6 +35,7 @@
214
215 from twisted.internet import defer
216 from twisted.internet.utils import getProcessValue
217+from twisted.internet.threads import deferToThread
218
219 AUTORUN_KEY = r"Software\Microsoft\Windows\CurrentVersion\Run"
220
221@@ -41,6 +43,7 @@
222 # beta is indeed present in the system, can be found in
223 # lp:ubuntuone-windows-installed/beta/install/UbuntuOne7.wxs
224 OLD_BETA_UID_KEY = '{13b7ee30-849c-11df-8395-0800200c9a66}'
225+PROPERTY_BUFFER_SIZE = 256
226 # error code used by ctypes.windll.msi
227 ERROR_MORE_DATA = 234
228 ERROR_SUCCESS = 0
229@@ -49,6 +52,10 @@
230 VERSION_STRING_PROPERTY = u'VersionString'
231
232
233+class MsiException(Exception):
234+ """Raised when there are msi issues."""
235+
236+
237 def uninstall_application():
238 """Uninstall Ubuntu One."""
239 if hasattr(sys, "frozen"):
240@@ -156,32 +163,61 @@
241 return [docs, music, pictures]
242
243
244+def get_property_for_product(product, property_name,
245+ buf_size=PROPERTY_BUFFER_SIZE):
246+ """Return the value of a given property from a product."""
247+ property_buffer = ctypes.create_unicode_buffer(buf_size)
248+ size = wintypes.DWORD(buf_size)
249+ result = ctypes.windll.msi.MsiGetProductInfoW(product, property_name,
250+ property_buffer,
251+ ctypes.byref(size))
252+ if result == ERROR_MORE_DATA:
253+ return get_property_for_product(product, property_name,
254+ 2 * buf_size)
255+ elif result == ERROR_SUCCESS:
256+ return property_buffer.value
257+ else:
258+ # The logic of the following statement is the following. The above code
259+ # queried the msi system for a property regarding the msi uid. This
260+ # uid is the code that uniquely identifies an app in the system. When
261+ # working with COM properties there are different errors to take into
262+ # account:
263+ # ERROR_SUCCESS: We managed to get the data from the property.
264+ # ERROR_MORE_DATA: The property does exist but the buffer allocated for
265+ # the result is too small.
266+ #
267+ # ERROR_UNKNOWN_PROPERTY: The property does not exist.
268+ # To the above errors we need to add ERROR_UNKNOWN_PRODUCT which is
269+ # particular to msi and states that the product does not exist.
270+ #
271+ # knowing the above we can deduce that if we do not get any of the
272+ # above error it means that such a property does not exists and
273+ # therefore we return None.
274+ return None
275+
276+
277 def is_old_beta_installed():
278 """Return whether the old beta is installed in the system."""
279 # we try to get the VersisonString for the uid, if we get an error it means
280 # that the product is not installed in the system.
281- buf_size = 256
282- uid_buffer = ctypes.create_unicode_buffer(OLD_BETA_UID_KEY)
283- property_buffer = ctypes.create_unicode_buffer(buf_size)
284- size = wintypes.DWORD(buf_size)
285- result = ctypes.windll.msi.MsiGetProductInfoW(uid_buffer,
286- VERSION_STRING_PROPERTY,
287- property_buffer,
288- ctypes.byref(size))
289- # The logic of the following statement is the following. The above code
290- # queried the msi system for a property regarding the old beta uid. This
291- # uid is the code that uniquely identifies an app in the system. When
292- # working with COM properties there are different errors to take into
293- # account:
294- # ERROR_SUCCESS: We managed to get the data from the property.
295- # ERROR_MORE_DATA: The property does exist but the buffer allocated for the
296- # result is too small.
297- # ERROR_UNKNOWN_PROPERTY: The property does not exist.
298- # To the above errors we need to add ERROR_UNKNOWN_PRODUCT which is
299- # particular to msi and states that the product does not exist.
300- #
301- # knowing the above we can deduce the existence of the application by
302- # checking if the ERROR_SUCCESS or ERROR_MORE_DATA were returned. This
303- # is because the fact that those errors are given state that the uid of the
304- # msi is present and that we can get the info.
305- return result in (ERROR_SUCCESS, ERROR_MORE_DATA)
306+ version_string = get_property_for_product(OLD_BETA_UID_KEY,
307+ VERSION_STRING_PROPERTY)
308+ return version_string is not None
309+
310+
311+@defer.inlineCallbacks
312+def uninstall_old_beta():
313+ """Remove the old beta from the user's machine."""
314+ # we use the msi lib to be able to uninstall apps
315+ property_name = u'LocalPackage'
316+ uninstall_path = get_property_for_product(OLD_BETA_UID_KEY, property_name)
317+ if uninstall_path is not None:
318+ # lets remove the package.
319+ command_line = u'REMOVE=ALL'
320+ result = yield deferToThread(ctypes.windll.msi.MsiInstallProductW,
321+ uninstall_path, command_line)
322+ if result != ERROR_SUCCESS:
323+ raise MsiException('Could not remove old beta.')
324+ else:
325+ # the local file is missing so we cannot un install it :(
326+ raise MsiException('Could not remove old beta.')

Subscribers

People subscribed via source and target branches