Merge lp:~evfool/software-properties/miscfixes2 into lp:software-properties

Proposed by Robert Roth
Status: Needs review
Proposed branch: lp:~evfool/software-properties/miscfixes2
Merge into: lp:software-properties
Diff against target: 455 lines (+143/-105)
7 files modified
add-apt-repository (+67/-59)
data/gtkbuilder/dialog-add.ui (+11/-36)
softwareproperties/SoftwareProperties.py (+5/-3)
softwareproperties/gtk/SoftwarePropertiesGtk.py (+2/-0)
softwareproperties/ppa.py (+18/-6)
tests/test_aptaddrepo.py (+36/-0)
tests/test_lp.py (+4/-1)
To merge this branch: bzr merge lp:~evfool/software-properties/miscfixes2
Reviewer Review Type Date Requested Status
Michael Vogt (community) Needs Information
Review via email: mp+92611@code.launchpad.net

Description of the change

This branch fixes the following bugs:
- Bug #496879 - returns non-0 result code from add-apt-repository whenever anything goes wrong, PPA can not be found, or signing key can not be found
- Bug #779287 - API url changed to the URL of the pubic API
- Bug #489488 - partially fixes this by adding the PPA display name as deb-line comment, thus the displayname will be shown in software-properties instead of the long and confusing PPA url
- Bug #930624 - add a title (Add) to the add-dialog (instead of the default software-properties-gtk) and sets the image of the Add Source button to respect the buttons-have-icons setting
- Bug #599801 - sort software sources from the other software tab alphabetically by their description, this way we get a more deterministic ordering than the default, thus users will be able to find the source they are looking for faster.

I have also made the following changes:
- removed a duplicated utf8 method(the same method has been declared twice in apt-add-repo),
- added another testcase in testlp to test invalid repo is handled correctly (no PPA info is returned)
- updated the error handling (try .. except HTTPError, URLError statements have been removed) to work with new curl based PPA info fetching (as curl does not throw an exception, but the result code can be queried separately) by only accepting HTTP 200 OK for now, other "partially successful" HTTP response codes should be added as required
- added new testaptaddrepo tests for apt-add-repository sanity checks, to see if adding invalid repo returns non-0 result code and working commands return 0 result codes

To post a comment you must log in.
749. By Robert Roth

Fix add source dialog consistency issues (LP: #930624)

750. By Robert Roth

Sort software sources alphabetically by their description (LP: #599801)

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

Thanks! This looks great. I tweaked some minor issues and added a FIXME for the new tests/test_aptaddrepo.py so that it can be run as non-root. Do you think you would have time to fix this small issue? If not I will give it a go later :)

review: Needs Information
751. By Robert Roth

Merged from trunk

Revision history for this message
Robert Roth (evfool) wrote :

Hi mvo, I have missed the status change on this proposal. I have merged from the current trunk now, and I can't see your FIXMEs, what should I fix here?

752. By Robert Roth

Merged from mvo's branch

753. By Robert Roth

Added apt-root dir CLI parameter for apt-add-repository

Revision history for this message
Robert Roth (evfool) wrote :

I have found your branch, merged from it, made a few adjustments, and added an apt-rootdir CLI parameter for apt-add-repository and tried to make it work in the test, however somehow I couldn't get the tests correctly working. Could you please take a look at the updated branch?

Unmerged revisions

753. By Robert Roth

Added apt-root dir CLI parameter for apt-add-repository

752. By Robert Roth

Merged from mvo's branch

751. By Robert Roth

Merged from trunk

750. By Robert Roth

Sort software sources alphabetically by their description (LP: #599801)

749. By Robert Roth

Fix add source dialog consistency issues (LP: #930624)

748. By Robert Roth

Also return non-null result code if PPA key validation fails (LP: #496879)

747. By Robert Roth

Add basic unit test for apt-add-repository

746. By Robert Roth

Add the display name of a PPA as deb entry comment (LP: #489488)

745. By Robert Roth

Use public Launchpad API (LP:#779287)

744. By Robert Roth

Fix error handling in new launchpad info retrieval (LP: #496879)

Preview Diff

[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
=== modified file 'add-apt-repository'
--- add-apt-repository 2012-03-07 06:43:56 +0000
+++ add-apt-repository 2012-05-11 09:20:22 +0000
@@ -4,6 +4,7 @@
4import sys4import sys
5import gettext5import gettext
6import locale6import locale
7import pycurl
78
8from softwareproperties.SoftwareProperties import SoftwareProperties9from softwareproperties.SoftwareProperties import SoftwareProperties
9from softwareproperties.ppa import DEFAULT_KEYSERVER, expand_ppa_line10from softwareproperties.ppa import DEFAULT_KEYSERVER, expand_ppa_line
@@ -24,7 +25,7 @@
24 return s.encode("utf-8", "ignore")25 return s.encode("utf-8", "ignore")
25 return unicode(s, "utf8", "ignore").encode("utf8")26 return unicode(s, "utf8", "ignore").encode("utf8")
2627
27def _maybe_suggest_ppa_name_based_on_user(user):28def _maybe_suggest_ppa_name_based_on_user(user, ppa_name):
28 try:29 try:
29 from launchpadlib.launchpad import Launchpad30 from launchpadlib.launchpad import Launchpad
30 lp = Launchpad.login_anonymously(lp_application_name, "production")31 lp = Launchpad.login_anonymously(lp_application_name, "production")
@@ -32,31 +33,61 @@
32 user_inst = lp.people[user]33 user_inst = lp.people[user]
33 entity_name = "team" if user_inst.is_team else "user"34 entity_name = "team" if user_inst.is_team else "user"
34 if len(user_inst.ppas) > 0:35 if len(user_inst.ppas) > 0:
35 print _("The %s named '%s' has no PPA named '%s'" 36 print _("The %s named '%s' has no PPA named '%s'" ) % (entity_name, user, ppa_name)
36 %(entity_name, user, ppa_name))
37 print _("Please choose from the following available PPAs:")37 print _("Please choose from the following available PPAs:")
38 for ppa in user_inst.ppas:38 for ppa in user_inst.ppas:
39 print _(" * '%s': %s" %(ppa.name, ppa.displayname))39 print _(" * '%s': %s" %(ppa.name, ppa.displayname))
40 else:40 else:
41 print _("The %s named '%s' does not have any PPA"41 print _("The %s named '%s' does not have any PPA repositories.") % (entity_name, user)
42 %(entity_name, user))
43 except KeyError:42 except KeyError:
44 pass43 pass
45 except ImportError:44 except ImportError:
46 print _("Please check that the PPA name or format is correct.")45 print _("Please check that the PPA name or format is correct.")
4746
4847def ask_user_about_ppa(line, is_removal):
4948
50def utf8(s):49 from softwareproperties.ppa import get_ppa_info_from_lp, LAUNCHPAD_PPA_API
51 """50 user, sep, ppa_name = line.split(":")[1].partition("/")
52 Takes a string or unicode object and returns a utf-8 encoded51 ppa_name = ppa_name or "ppa"
53 string, errors are ignored52 try:
54 """53 ppa_info = get_ppa_info_from_lp(user, ppa_name)
55 if s is None:54 except pycurl.error:
56 return None55 print _("Cannot access PPA (%s) to get PPA information, "
57 if isinstance(s, unicode):56 "please check your internet connection.") % \
58 return s.encode("utf-8", "ignore")57 (LAUNCHPAD_PPA_API % (user, ppa_name))
59 return unicode(s, "utf8", "ignore").encode("utf8")58 return (1, None)
59 # ppa not found
60 if not ppa_info:
61 print _("The specified PPA (%s) could not be found. ") % line
62 if user.startswith("~"):
63 print _("Did you mean 'ppa:%s/%s' ?" %(user[1:], ppa_name))
64 return (1, None) # Exit because the user cannot be correct
65 # If the PPA does not exist, then try to find if the user/team
66 # exists. If it exists, list down the PPAs
67 _maybe_suggest_ppa_name_based_on_user(user, ppa_name)
68 return (1, None)
69 # private PPAs are not supported
70 if "private" in ppa_info and ppa_info["private"]:
71 print _("Adding private PPAs is not supported currently")
72 return (1, None)
73
74 if is_removal:
75 print _("You are about to remove the following PPA from your system:")
76 else:
77 print _("You are about to add the following PPA to your system:")
78 print " %s" % utf8(ppa_info["description"] or "")
79 print _(" More info: %s") % ppa_info["web_link"]
80 if (sys.stdin.isatty() and
81 not "FORCE_ADD_APT_REPOSITORY" in os.environ):
82 if is_removal:
83 print _("Press [ENTER] to continue or ctrl-c to cancel removing it")
84 else:
85 print _("Press [ENTER] to continue or ctrl-c to cancel adding it")
86 try:
87 sys.stdin.readline()
88 except KeyboardInterrupt:
89 return (2, None)
90 return (0, ppa_info["displayname"])
6091
61if __name__ == "__main__":92if __name__ == "__main__":
62 try:93 try:
@@ -100,9 +131,16 @@
100 parser.add_option("-y", "--yes", action="store_true",131 parser.add_option("-y", "--yes", action="store_true",
101 dest="assume_yes", default=False,132 dest="assume_yes", default=False,
102 help="Assume yes to all queries")133 help="Assume yes to all queries")
134 parser.add_option("-a", "--aptrootdir",
135 action="store", dest="aptrootdir", type="string", default=None,
136 help="Use the given directory as apt root (for testing purposes)")
103 (options, args) = parser.parse_args()137 (options, args) = parser.parse_args()
104138
105 if os.geteuid() != 0:139 if options.aptrootdir:
140 if not os.access(options.aptrootdir, os.W_OK):
141 print _("Error: you don't have write access to the sources.list file %s, try running as root")%options.aptrootdir
142 sys.exit(1)
143 elif os.geteuid() != 0:
106 print _("Error: must run as root")144 print _("Error: must run as root")
107 sys.exit(1)145 sys.exit(1)
108146
@@ -118,46 +156,16 @@
118156
119 # display PPA info (if needed)157 # display PPA info (if needed)
120 if line.startswith("ppa:") and not options.assume_yes:158 if line.startswith("ppa:") and not options.assume_yes:
121 from softwareproperties.ppa import get_ppa_info_from_lp, LAUNCHPAD_PPA_API159 (error_code, display_name) = ask_user_about_ppa(line, options.remove)
122 user, sep, ppa_name = line.split(":")[1].partition("/")160 if error_code != 0:
123 ppa_name = ppa_name or "ppa"161 sys.exit(error_code)
124 try:162 line += "#" + display_name
125 ppa_info = get_ppa_info_from_lp(user, ppa_name)
126 except HTTPError:
127 print _("Cannot add PPA: '%s'.") % line
128 if user.startswith("~"):
129 print _("Did you mean 'ppa:%s/%s' ?" %(user[1:], ppa_name))
130 sys.exit(1) # Exit because the user cannot be correct
131 # If the PPA does not exist, then try to find if the user/team
132 # exists. If it exists, list down the PPAs
133 _maybe_suggest_ppa_name_based_on_user(user)
134 sys.exit(1)
135 except (ValueError, URLError):
136 print _("Cannot access PPA (%s) to get PPA information, "
137 "please check your internet connection.") % \
138 (LAUNCHPAD_PPA_API % (user, ppa_name))
139 sys.exit(1)
140 # private PPAs are not supported
141 if "private" in ppa_info and ppa_info["private"]:
142 print _("Adding private PPAs is not supported currently")
143 sys.exit(1)
144
145 if options.remove:
146 print _("You are about to remove the following PPA from your system:")
147 else:
148 print _("You are about to add the following PPA to your system:")
149 print " %s" % utf8(ppa_info["description"] or "")
150 print _(" More info: %s") % ppa_info["web_link"]
151 if (sys.stdin.isatty() and
152 not "FORCE_ADD_APT_REPOSITORY" in os.environ):
153 if options.remove:
154 print _("Press [ENTER] to continue or ctrl-c to cancel removing it")
155 else:
156 print _("Press [ENTER] to continue or ctrl-c to cancel adding it")
157 sys.stdin.readline()
158163
159 # add it164 # add it
160 sp = SoftwareProperties(options=options)165 if options.aptrootdir:
166 sp = SoftwareProperties(options=options, rootdir=options.aptrootdir)
167 else:
168 sp = SoftwareProperties(options=options)
161 if options.remove:169 if options.remove:
162 (line, file) = expand_ppa_line(line.strip(), sp.distro.codename)170 (line, file) = expand_ppa_line(line.strip(), sp.distro.codename)
163 deb_line = sp.expand_http_line(line)171 deb_line = sp.expand_http_line(line)
@@ -167,14 +175,14 @@
167 try:175 try:
168 sp.remove_source(deb_entry)176 sp.remove_source(deb_entry)
169 except ValueError:177 except ValueError:
170 print _("Error: '%s' doesn't exist in a sourcelist file" % deb_line)178 print _("Error: '%s' doesn't exist in a sourcelist file") % deb_line
171 try:179 try:
172 sp.remove_source(debsrc_entry)180 sp.remove_source(debsrc_entry)
173 except ValueError:181 except ValueError:
174 print _("Error: '%s' doesn't exist in a sourcelist file" % debsrc_line)182 print _("Error: '%s' doesn't exist in a sourcelist file") % debsrc_line
175183
176 else:184 else:
177 if not sp.add_source_from_line(line):185 if not sp.add_source_from_line(line):
178 print _("Error: '%s' invalid" % line)186 print _("Error: could not add '%s'") % line
179 sys.exit(1)187 sys.exit(1)
180 sp.sourceslist.save()188 sp.sourceslist.save()
181189
=== modified file 'data/gtkbuilder/dialog-add.ui'
--- data/gtkbuilder/dialog-add.ui 2011-06-22 15:03:09 +0000
+++ data/gtkbuilder/dialog-add.ui 2012-05-11 09:20:22 +0000
@@ -2,11 +2,17 @@
2<interface>2<interface>
3 <!-- interface-requires gtk+ 2.12 -->3 <!-- interface-requires gtk+ 2.12 -->
4 <!-- interface-naming-policy toplevel-contextual -->4 <!-- interface-naming-policy toplevel-contextual -->
5 <object class="GtkImage" id="add_image">
6 <property name="visible">True</property>
7 <property name="can_focus">False</property>
8 <property name="stock">gtk-add</property>
9 </object>
5 <object class="GtkDialog" id="dialog_add_custom">10 <object class="GtkDialog" id="dialog_add_custom">
6 <property name="border_width">6</property>11 <property name="border_width">6</property>
7 <property name="resizable">False</property>12 <property name="resizable">False</property>
8 <property name="modal">True</property>13 <property name="modal">True</property>
9 <property name="type_hint">dialog</property>14 <property name="type_hint">dialog</property>
15 <property name="title" translatable="yes">Add</property>
10 <property name="skip_taskbar_hint">True</property>16 <property name="skip_taskbar_hint">True</property>
11 <child internal-child="vbox">17 <child internal-child="vbox">
12 <object class="GtkVBox" id="dialog-vbox2">18 <object class="GtkVBox" id="dialog-vbox2">
@@ -126,6 +132,8 @@
126 </child>132 </child>
127 <child>133 <child>
128 <object class="GtkButton" id="button_add_source">134 <object class="GtkButton" id="button_add_source">
135 <property name="label" translatable="yes">_Add Source</property>
136 <property name="use_action_appearance">False</property>
129 <property name="visible">True</property>137 <property name="visible">True</property>
130 <property name="sensitive">False</property>138 <property name="sensitive">False</property>
131 <property name="can_focus">True</property>139 <property name="can_focus">True</property>
@@ -133,42 +141,9 @@
133 <property name="can_default">True</property>141 <property name="can_default">True</property>
134 <property name="has_default">True</property>142 <property name="has_default">True</property>
135 <property name="receives_default">False</property>143 <property name="receives_default">False</property>
136 <child>144 <property name="use_action_appearance">False</property>
137 <object class="GtkAlignment" id="alignment1">145 <property name="image">add_image</property>
138 <property name="visible">True</property>146 <property name="use_underline">True</property>
139 <property name="xscale">0</property>
140 <property name="yscale">0</property>
141 <child>
142 <object class="GtkHBox" id="hbox10">
143 <property name="visible">True</property>
144 <property name="spacing">2</property>
145 <child>
146 <object class="GtkImage" id="image2">
147 <property name="visible">True</property>
148 <property name="stock">gtk-add</property>
149 </object>
150 <packing>
151 <property name="expand">False</property>
152 <property name="fill">False</property>
153 <property name="position">0</property>
154 </packing>
155 </child>
156 <child>
157 <object class="GtkLabel" id="label35">
158 <property name="visible">True</property>
159 <property name="label" translatable="yes">_Add Source</property>
160 <property name="use_underline">True</property>
161 </object>
162 <packing>
163 <property name="expand">False</property>
164 <property name="fill">False</property>
165 <property name="position">1</property>
166 </packing>
167 </child>
168 </object>
169 </child>
170 </object>
171 </child>
172 </object>147 </object>
173 <packing>148 <packing>
174 <property name="expand">False</property>149 <property name="expand">False</property>
175150
=== modified file 'softwareproperties/SoftwareProperties.py'
--- softwareproperties/SoftwareProperties.py 2012-03-07 08:42:03 +0000
+++ softwareproperties/SoftwareProperties.py 2012-05-11 09:20:22 +0000
@@ -491,7 +491,7 @@
491 for c in source.comps:491 for c in source.comps:
492 contents += " %s" % c492 contents += " %s" % c
493 if source.type in ("deb-src", "rpm-src"):493 if source.type in ("deb-src", "rpm-src"):
494 contents += " %s" % _("(Source Code)")494 contents += " (%s)" % _("Source Code")
495 return contents495 return contents
496 else:496 else:
497 # try to make use of a corresponding template497 # try to make use of a corresponding template
@@ -697,6 +697,8 @@
697 deb_line = self.expand_http_line(deb_line)697 deb_line = self.expand_http_line(deb_line)
698 debsrc_line = 'deb-src' + deb_line[3:]698 debsrc_line = 'deb-src' + deb_line[3:]
699 new_deb_entry = SourceEntry(deb_line, file)699 new_deb_entry = SourceEntry(deb_line, file)
700 if new_deb_entry.comment:
701 debsrc_line += " (%s)" % _("Source Code")
700 new_debsrc_entry = SourceEntry(debsrc_line, file)702 new_debsrc_entry = SourceEntry(debsrc_line, file)
701 if new_deb_entry.invalid or new_debsrc_entry.invalid:703 if new_deb_entry.invalid or new_debsrc_entry.invalid:
702 return False704 return False
@@ -719,8 +721,8 @@
719 if worker:721 if worker:
720 # wait for GPG key to be downloaded722 # wait for GPG key to be downloaded
721 worker.join(30)723 worker.join(30)
722 return True724 return worker.success
723725
724 def remove_source(self, source):726 def remove_source(self, source):
725 """Remove the given source"""727 """Remove the given source"""
726 # first find the source object if we got a string728 # first find the source object if we got a string
727729
=== modified file 'softwareproperties/gtk/SoftwarePropertiesGtk.py'
--- softwareproperties/gtk/SoftwarePropertiesGtk.py 2012-03-07 09:13:48 +0000
+++ softwareproperties/gtk/SoftwarePropertiesGtk.py 2012-05-11 09:20:22 +0000
@@ -572,6 +572,7 @@
572 GObject.TYPE_PYOBJECT,572 GObject.TYPE_PYOBJECT,
573 GObject.TYPE_BOOLEAN,573 GObject.TYPE_BOOLEAN,
574 GObject.TYPE_BOOLEAN)574 GObject.TYPE_BOOLEAN)
575 self.source_store.set_sort_column_id(STORE_DESCRIPTION, Gtk.SortType.ASCENDING)
575 self.treeview_sources.set_model(self.source_store)576 self.treeview_sources.set_model(self.source_store)
576 self.treeview_sources.set_row_separator_func(self.is_separator,577 self.treeview_sources.set_row_separator_func(self.is_separator,
577 STORE_SEPARATOR)578 STORE_SEPARATOR)
@@ -582,6 +583,7 @@
582 col_desc = Gtk.TreeViewColumn(_("Software Sources"), cell_desc,583 col_desc = Gtk.TreeViewColumn(_("Software Sources"), cell_desc,
583 markup=COLUMN_DESC)584 markup=COLUMN_DESC)
584 col_desc.set_max_width(1000)585 col_desc.set_max_width(1000)
586 col_desc.set_sort_column_id(STORE_DESCRIPTION)
585587
586 cell_toggle = Gtk.CellRendererToggle()588 cell_toggle = Gtk.CellRendererToggle()
587 cell_toggle.set_property("xpad", 2)589 cell_toggle.set_property("xpad", 2)
588590
=== modified file 'softwareproperties/ppa.py'
--- softwareproperties/ppa.py 2012-01-31 15:51:15 +0000
+++ softwareproperties/ppa.py 2012-05-11 09:20:22 +0000
@@ -25,10 +25,11 @@
25import subprocess25import subprocess
26from threading import Thread26from threading import Thread
27import pycurl27import pycurl
28from httplib import OK as HTTP_200_OK
2829
29DEFAULT_KEYSERVER = "hkp://keyserver.ubuntu.com:80/"30DEFAULT_KEYSERVER = "hkp://keyserver.ubuntu.com:80/"
30# maintained until 201531# maintained until 2015
31LAUNCHPAD_PPA_API = 'https://launchpad.net/api/1.0/~%s/+archive/%s'32LAUNCHPAD_PPA_API = 'https://api.launchpad.net/1.0/~%s/+archive/%s'
32# None means use pycurl default 33# None means use pycurl default
33LAUNCHPAD_PPA_CERT = None34LAUNCHPAD_PPA_CERT = None
3435
@@ -45,13 +46,18 @@
45 # via some sort of API, see LP #385129)46 # via some sort of API, see LP #385129)
46 abrev = abrev.split(":")[1]47 abrev = abrev.split(":")[1]
47 ppa_owner = abrev.split("/")[0]48 ppa_owner = abrev.split("/")[0]
49 comment = None
50 if abrev.find("#") != -1:
51 comment = abrev.split("#")[1]
48 try:52 try:
49 ppa_name = abrev.split("/")[1]53 ppa_name = abrev.split("/")[1].split("#")[0]
50 except IndexError, e:54 except IndexError, e:
51 ppa_name = "ppa"55 ppa_name = "ppa"
52 sourceslistd = apt_pkg.config.find_dir("Dir::Etc::sourceparts")56 sourceslistd = apt_pkg.config.find_dir("Dir::Etc::sourceparts")
53 line = "deb http://ppa.launchpad.net/%s/%s/ubuntu %s main" % (57 line = "deb http://ppa.launchpad.net/%s/%s/ubuntu %s main" % (
54 ppa_owner, ppa_name, distro_codename)58 ppa_owner, ppa_name, distro_codename)
59 if comment:
60 line += "#" + comment
55 filename = "%s/%s-%s-%s.list" % (61 filename = "%s/%s-%s-%s.list" % (
56 sourceslistd, encode(ppa_owner), encode(ppa_name), distro_codename)62 sourceslistd, encode(ppa_owner), encode(ppa_name), distro_codename)
57 return (line, filename)63 return (line, filename)
@@ -78,9 +84,12 @@
78 curl.setopt(pycurl.URL, str(lp_url))84 curl.setopt(pycurl.URL, str(lp_url))
79 curl.setopt(pycurl.HTTPHEADER, ["Accept: application/json"])85 curl.setopt(pycurl.HTTPHEADER, ["Accept: application/json"])
80 curl.perform()86 curl.perform()
87 return_code = curl.getinfo(pycurl.HTTP_CODE);
81 curl.close()88 curl.close()
82 lp_page = callback.contents89 if return_code == HTTP_200_OK:
83 return json.loads(lp_page)90 lp_page = callback.contents
91 return json.loads(lp_page)
92 return None
8493
85class AddPPASigningKeyThread(Thread):94class AddPPASigningKeyThread(Thread):
86 " thread class for adding the signing key in the background "95 " thread class for adding the signing key in the background "
@@ -88,11 +97,12 @@
88 def __init__(self, ppa_path, keyserver=None):97 def __init__(self, ppa_path, keyserver=None):
89 Thread.__init__(self)98 Thread.__init__(self)
90 self.ppa_path = ppa_path99 self.ppa_path = ppa_path
100 self.success = None
91 self.keyserver = (keyserver if keyserver is not None101 self.keyserver = (keyserver if keyserver is not None
92 else DEFAULT_KEYSERVER)102 else DEFAULT_KEYSERVER)
93 103
94 def run(self):104 def run(self):
95 self.add_ppa_signing_key(self.ppa_path)105 self.success = self.add_ppa_signing_key(self.ppa_path)
96 106
97 def add_ppa_signing_key(self, ppa_path):107 def add_ppa_signing_key(self, ppa_path):
98 """Query and add the corresponding PPA signing key.108 """Query and add the corresponding PPA signing key.
@@ -112,9 +122,11 @@
112 print "Error: can't find signing_key_fingerprint at %s" % lp_url122 print "Error: can't find signing_key_fingerprint at %s" % lp_url
113 return False123 return False
114 res = subprocess.call(124 res = subprocess.call(
115 ["apt-key", "adv", 125 ["apt-key", "adv",
116 "--keyserver", self.keyserver,126 "--keyserver", self.keyserver,
117 "--recv", signing_key_fingerprint])127 "--recv", signing_key_fingerprint])
128 if res != 0:
129 print "Error: can't retrieve key file for the PPA's signing_key_fingerprint"
118 return (res == 0)130 return (res == 0)
119131
120132
121133
=== added file 'tests/test_aptaddrepo.py'
--- tests/test_aptaddrepo.py 1970-01-01 00:00:00 +0000
+++ tests/test_aptaddrepo.py 2012-05-11 09:20:22 +0000
@@ -0,0 +1,36 @@
1#!/usr/bin/python
2
3import pycurl
4import unittest
5import sys
6from subprocess import call
7sys.path.insert(0, "..")
8
9class TestAptAddRepo(unittest.TestCase):
10
11 # FIXME: we need to add a --datadir argument for add-apt-repository
12 # so that we can run this test as non-root
13
14 def test_adding_invalid_ppa(self):
15 resultcode = call(["../add-apt-repository",
16 "-a" , "./aptroot",
17 "ppa:mvo/inexistent-testing-ppa"])
18 self.assertNotEquals(resultcode, 0)
19
20 def test_ppa_add(self):
21 resultcode = call(["../add-apt-repository",
22 "-y",
23 "-a" , "./aptroot",
24 "ppa:mvo/ppa"])
25 self.assertEquals(resultcode, 0)
26
27 def test_ppa_remove(self):
28 resultcode = call(["../add-apt-repository",
29 "-y", "-r",
30 "-a" , "./aptroot",
31 "ppa:mvo/ppa"])
32 self.assertEquals(resultcode, 0)
33
34
35if __name__ == "__main__":
36 unittest.main()
037
=== modified file 'tests/test_lp.py'
--- tests/test_lp.py 2012-01-31 15:51:15 +0000
+++ tests/test_lp.py 2012-05-11 09:20:22 +0000
@@ -19,7 +19,10 @@
19 self.assertRaises(19 self.assertRaises(
20 pycurl.error, softwareproperties.ppa.get_ppa_info_from_lp, "mvo", "ppa")20 pycurl.error, softwareproperties.ppa.get_ppa_info_from_lp, "mvo", "ppa")
2121
22 22 def test_invalid_ppa(self):
23 # retrieve info for an inexistent PPA which will return anything else than HTTP 200 OK
24 info = softwareproperties.ppa.get_ppa_info_from_lp("evfool", "inexistent-testing-ppa")
25 self.assertEquals(info, None)
2326
2427
25if __name__ == "__main__":28if __name__ == "__main__":

Subscribers

People subscribed via source and target branches

to status/vote changes: