Merge lp:~ack/landscape-client/package-description-decode-fix into lp:~landscape/landscape-client/trunk
- package-description-decode-fix
- Merge into trunk
Status: | Merged | ||||
---|---|---|---|---|---|
Approved by: | Alberto Donato | ||||
Approved revision: | 929 | ||||
Merged at revision: | 928 | ||||
Proposed branch: | lp:~ack/landscape-client/package-description-decode-fix | ||||
Merge into: | lp:~landscape/landscape-client/trunk | ||||
Diff against target: |
238 lines (+46/-29) 4 files modified
landscape/package/skeleton.py (+4/-2) landscape/package/tests/helpers.py (+12/-6) landscape/package/tests/test_skeleton.py (+29/-20) landscape/tests/helpers.py (+1/-1) |
||||
To merge this branch: | bzr merge lp:~ack/landscape-client/package-description-decode-fix | ||||
Related bugs: |
|
Reviewer | Review Type | Date Requested | Status |
---|---|---|---|
Simon Poirier (community) | Approve | ||
🤖 Landscape Builder | test results | Approve | |
Free Ekanayaka (community) | Approve | ||
Review via email: mp+314444@code.launchpad.net |
Commit message
Avoid double-decoding package descriptions in build_skeleton_apt, which causes an error with Xenial python-apt.
Description of the change
Avoid double-decoding package descriptions in build_skeleton_apt, which causes an error with Xenial python-apt.
Note that python-apt replaces non-ascii chars in the description with "?".
This happens because no locale is set, but we can't arbitrarily set one in the client and we don't want to report localized versions of package descriptions.
🤖 Landscape Builder (landscape-builder) : | # |
🤖 Landscape Builder (landscape-builder) wrote : | # |
Free Ekanayaka (free.ekanayaka) wrote : | # |
Nice, +1
- 928. By Alberto Donato
-
Add comment.
🤖 Landscape Builder (landscape-builder) : | # |
🤖 Landscape Builder (landscape-builder) wrote : | # |
Command: TRIAL_ARGS=-j4 make check
Result: Success
Revno: 928
Branch: lp:~ack/landscape-client/package-description-decode-fix
Jenkins: https:/
Simon Poirier (simpoir) wrote : | # |
The change looks good, but your test doesn't seem to be calling build_skeleton(). And quick coverage run tells me your code is not exercised through test.
- 929. By Alberto Donato
-
Fix test (Simon's review)
Alberto Donato (ack) wrote : | # |
> The change looks good, but your test doesn't seem to be calling
> build_skeleton(). And quick coverage run tells me your code is not exercised
> through test.
Good catch, I think something got messed up while I was experimenting with the test.
It's fixed now. I also dropped the self.build_
Thanks!
🤖 Landscape Builder (landscape-builder) : | # |
🤖 Landscape Builder (landscape-builder) wrote : | # |
Command: TRIAL_ARGS=-j4 make check
Result: Fail
Revno: 929
Branch: lp:~ack/landscape-client/package-description-decode-fix
Jenkins: https:/
🤖 Landscape Builder (landscape-builder) : | # |
🤖 Landscape Builder (landscape-builder) wrote : | # |
Command: TRIAL_ARGS=-j4 make check
Result: Success
Revno: 929
Branch: lp:~ack/landscape-client/package-description-decode-fix
Jenkins: https:/
Preview Diff
1 | === modified file 'landscape/package/skeleton.py' | |||
2 | --- landscape/package/skeleton.py 2013-05-31 12:27:27 +0000 | |||
3 | +++ landscape/package/skeleton.py 2017-01-12 08:01:46 +0000 | |||
4 | @@ -67,7 +67,6 @@ | |||
5 | 67 | self._hash = package_hash | 67 | self._hash = package_hash |
6 | 68 | 68 | ||
7 | 69 | 69 | ||
8 | 70 | |||
9 | 71 | def relation_to_string(relation_tuple): | 70 | def relation_to_string(relation_tuple): |
10 | 72 | """Convert an apt relation to a string representation. | 71 | """Convert an apt relation to a string representation. |
11 | 73 | 72 | ||
12 | @@ -150,5 +149,8 @@ | |||
13 | 150 | if with_unicode: | 149 | if with_unicode: |
14 | 151 | skeleton.section = skeleton.section.decode("utf-8") | 150 | skeleton.section = skeleton.section.decode("utf-8") |
15 | 152 | skeleton.summary = skeleton.summary.decode("utf-8") | 151 | skeleton.summary = skeleton.summary.decode("utf-8") |
17 | 153 | skeleton.description = skeleton.description.decode("utf-8") | 152 | # Avoid double-decoding package descriptions in build_skeleton_apt, |
18 | 153 | # which causes an error with newer python-apt (Xenial onwards) | ||
19 | 154 | if not isinstance(skeleton.description, unicode): | ||
20 | 155 | skeleton.description = skeleton.description.decode("utf-8") | ||
21 | 154 | return skeleton | 156 | return skeleton |
22 | 155 | 157 | ||
23 | === modified file 'landscape/package/tests/helpers.py' | |||
24 | --- landscape/package/tests/helpers.py 2016-07-06 11:06:09 +0000 | |||
25 | +++ landscape/package/tests/helpers.py 2017-01-12 08:01:46 +0000 | |||
26 | @@ -30,7 +30,8 @@ | |||
27 | 30 | test_case._hash_packages_by_name = self._hash_packages_by_name | 30 | test_case._hash_packages_by_name = self._hash_packages_by_name |
28 | 31 | 31 | ||
29 | 32 | def _add_package(self, packages_file, name, architecture="all", | 32 | def _add_package(self, packages_file, name, architecture="all", |
31 | 33 | version="1.0", control_fields=None): | 33 | version="1.0", description="description", |
32 | 34 | control_fields=None): | ||
33 | 34 | if control_fields is None: | 35 | if control_fields is None: |
34 | 35 | control_fields = {} | 36 | control_fields = {} |
35 | 36 | package_stanza = textwrap.dedent(""" | 37 | package_stanza = textwrap.dedent(""" |
36 | @@ -42,9 +43,12 @@ | |||
37 | 42 | Architecture: %(architecture)s | 43 | Architecture: %(architecture)s |
38 | 43 | Source: source | 44 | Source: source |
39 | 44 | Version: %(version)s | 45 | Version: %(version)s |
43 | 45 | Description: description | 46 | Description: short description |
44 | 46 | """ % {"name": name, "version": version, | 47 | %(description)s |
45 | 47 | "architecture": architecture}) | 48 | """ % { |
46 | 49 | "name": name, "version": version, | ||
47 | 50 | "architecture": architecture, | ||
48 | 51 | "description": description.encode("utf-8")}) | ||
49 | 48 | package_stanza = apt_pkg.rewrite_section( | 52 | package_stanza = apt_pkg.rewrite_section( |
50 | 49 | apt_pkg.TagSection(package_stanza), apt_pkg.REWRITE_PACKAGE_ORDER, | 53 | apt_pkg.TagSection(package_stanza), apt_pkg.REWRITE_PACKAGE_ORDER, |
51 | 50 | control_fields.items()) | 54 | control_fields.items()) |
52 | @@ -72,7 +76,8 @@ | |||
53 | 72 | append_file(self.dpkg_status, status + "\n\n") | 76 | append_file(self.dpkg_status, status + "\n\n") |
54 | 73 | 77 | ||
55 | 74 | def _add_package_to_deb_dir(self, path, name, architecture="all", | 78 | def _add_package_to_deb_dir(self, path, name, architecture="all", |
57 | 75 | version="1.0", control_fields=None): | 79 | version="1.0", description="description", |
58 | 80 | control_fields=None): | ||
59 | 76 | """Add fake package information to a directory. | 81 | """Add fake package information to a directory. |
60 | 77 | 82 | ||
61 | 78 | There will only be basic information about the package | 83 | There will only be basic information about the package |
62 | @@ -83,7 +88,8 @@ | |||
63 | 83 | control_fields = {} | 88 | control_fields = {} |
64 | 84 | self._add_package( | 89 | self._add_package( |
65 | 85 | os.path.join(path, "Packages"), name, architecture=architecture, | 90 | os.path.join(path, "Packages"), name, architecture=architecture, |
67 | 86 | version=version, control_fields=control_fields) | 91 | version=version, description=description, |
68 | 92 | control_fields=control_fields) | ||
69 | 87 | 93 | ||
70 | 88 | def _touch_packages_file(self, deb_dir): | 94 | def _touch_packages_file(self, deb_dir): |
71 | 89 | """Make sure the Packages file gets a newer mtime value. | 95 | """Make sure the Packages file gets a newer mtime value. |
72 | 90 | 96 | ||
73 | === modified file 'landscape/package/tests/test_skeleton.py' | |||
74 | --- landscape/package/tests/test_skeleton.py 2013-05-31 07:57:06 +0000 | |||
75 | +++ landscape/package/tests/test_skeleton.py 2017-01-12 08:01:46 +0000 | |||
76 | @@ -59,18 +59,14 @@ | |||
77 | 59 | package = self.facade._cache[name] | 59 | package = self.facade._cache[name] |
78 | 60 | return package.candidate | 60 | return package.candidate |
79 | 61 | 61 | ||
80 | 62 | def build_skeleton(self, *args, **kwargs): | ||
81 | 63 | """Build the skeleton to be tested.""" | ||
82 | 64 | return build_skeleton_apt(*args, **kwargs) | ||
83 | 65 | |||
84 | 66 | def test_build_skeleton(self): | 62 | def test_build_skeleton(self): |
85 | 67 | """ | 63 | """ |
87 | 68 | C{build_skeleton} builds a C{PackageSkeleton} from a package. If | 64 | build_skeleton_apt builds a C{PackageSkeleton} from a package. If |
88 | 69 | with_info isn't passed, C{section}, C{summary}, C{description}, | 65 | with_info isn't passed, C{section}, C{summary}, C{description}, |
89 | 70 | C{size} and C{installed_size} will be C{None}. | 66 | C{size} and C{installed_size} will be C{None}. |
90 | 71 | """ | 67 | """ |
91 | 72 | pkg1 = self.get_package("name1") | 68 | pkg1 = self.get_package("name1") |
93 | 73 | skeleton = self.build_skeleton(pkg1) | 69 | skeleton = build_skeleton_apt(pkg1) |
94 | 74 | self.assertEqual("name1", skeleton.name) | 70 | self.assertEqual("name1", skeleton.name) |
95 | 75 | self.assertEqual("version1-release1", skeleton.version) | 71 | self.assertEqual("version1-release1", skeleton.version) |
96 | 76 | self.assertEqual(None, skeleton.section) | 72 | self.assertEqual(None, skeleton.section) |
97 | @@ -90,35 +86,35 @@ | |||
98 | 90 | 86 | ||
99 | 91 | def test_build_skeleton_without_unicode(self): | 87 | def test_build_skeleton_without_unicode(self): |
100 | 92 | """ | 88 | """ |
102 | 93 | If C{with_unicode} isn't passed to C{build_skeleton}, the name | 89 | If C{with_unicode} isn't passed to build_skeleton_apt, the name |
103 | 94 | and version of the skeleton are byte strings. The hash doesn't | 90 | and version of the skeleton are byte strings. The hash doesn't |
104 | 95 | change, though. | 91 | change, though. |
105 | 96 | """ | 92 | """ |
106 | 97 | pkg1 = self.get_package("name1") | 93 | pkg1 = self.get_package("name1") |
108 | 98 | skeleton = self.build_skeleton(pkg1) | 94 | skeleton = build_skeleton_apt(pkg1) |
109 | 99 | self.assertTrue(isinstance(skeleton.name, str)) | 95 | self.assertTrue(isinstance(skeleton.name, str)) |
110 | 100 | self.assertTrue(isinstance(skeleton.version, str)) | 96 | self.assertTrue(isinstance(skeleton.version, str)) |
111 | 101 | self.assertEqual(HASH1, skeleton.get_hash()) | 97 | self.assertEqual(HASH1, skeleton.get_hash()) |
112 | 102 | 98 | ||
113 | 103 | def test_build_skeleton_with_unicode(self): | 99 | def test_build_skeleton_with_unicode(self): |
114 | 104 | """ | 100 | """ |
116 | 105 | If C{with_unicode} is passed to C{build_skeleton}, the name | 101 | If C{with_unicode} is passed to build_skeleton_apt, the name |
117 | 106 | and version of the skeleton are unicode strings. | 102 | and version of the skeleton are unicode strings. |
118 | 107 | """ | 103 | """ |
119 | 108 | pkg1 = self.get_package("name1") | 104 | pkg1 = self.get_package("name1") |
121 | 109 | skeleton = self.build_skeleton(pkg1, with_unicode=True) | 105 | skeleton = build_skeleton_apt(pkg1, with_unicode=True) |
122 | 110 | self.assertTrue(isinstance(skeleton.name, unicode)) | 106 | self.assertTrue(isinstance(skeleton.name, unicode)) |
123 | 111 | self.assertTrue(isinstance(skeleton.version, unicode)) | 107 | self.assertTrue(isinstance(skeleton.version, unicode)) |
124 | 112 | self.assertEqual(HASH1, skeleton.get_hash()) | 108 | self.assertEqual(HASH1, skeleton.get_hash()) |
125 | 113 | 109 | ||
126 | 114 | def test_build_skeleton_with_info(self): | 110 | def test_build_skeleton_with_info(self): |
127 | 115 | """ | 111 | """ |
129 | 116 | If C{with_info} is passed to C{build_skeleton}, C{section}, | 112 | If C{with_info} is passed to build_skeleton_apt, C{section}, |
130 | 117 | C{summary}, C{description} and the size fields will be extracted | 113 | C{summary}, C{description} and the size fields will be extracted |
131 | 118 | from the package. | 114 | from the package. |
132 | 119 | """ | 115 | """ |
133 | 120 | pkg1 = self.get_package("name1") | 116 | pkg1 = self.get_package("name1") |
135 | 121 | skeleton = self.build_skeleton(pkg1, with_info=True) | 117 | skeleton = build_skeleton_apt(pkg1, with_info=True) |
136 | 122 | self.assertEqual("Group1", skeleton.section) | 118 | self.assertEqual("Group1", skeleton.section) |
137 | 123 | self.assertEqual("Summary1", skeleton.summary) | 119 | self.assertEqual("Summary1", skeleton.summary) |
138 | 124 | self.assertEqual("Description1", skeleton.description) | 120 | self.assertEqual("Description1", skeleton.description) |
139 | @@ -128,11 +124,11 @@ | |||
140 | 128 | def test_build_skeleton_with_unicode_and_extra_info(self): | 124 | def test_build_skeleton_with_unicode_and_extra_info(self): |
141 | 129 | """ | 125 | """ |
142 | 130 | If C{with_unicode} and C{with_info} are passed to | 126 | If C{with_unicode} and C{with_info} are passed to |
144 | 131 | C{build_skeleton}, the name, version and the extra info of the | 127 | build_skeleton_apt, the name, version and the extra info of the |
145 | 132 | skeleton are unicode strings. | 128 | skeleton are unicode strings. |
146 | 133 | """ | 129 | """ |
147 | 134 | pkg1 = self.get_package("name1") | 130 | pkg1 = self.get_package("name1") |
149 | 135 | skeleton = self.build_skeleton(pkg1, with_unicode=True, with_info=True) | 131 | skeleton = build_skeleton_apt(pkg1, with_unicode=True, with_info=True) |
150 | 136 | self.assertTrue(isinstance(skeleton.name, unicode)) | 132 | self.assertTrue(isinstance(skeleton.name, unicode)) |
151 | 137 | self.assertTrue(isinstance(skeleton.version, unicode)) | 133 | self.assertTrue(isinstance(skeleton.version, unicode)) |
152 | 138 | self.assertTrue(isinstance(skeleton.section, unicode)) | 134 | self.assertTrue(isinstance(skeleton.section, unicode)) |
153 | @@ -140,13 +136,26 @@ | |||
154 | 140 | self.assertTrue(isinstance(skeleton.description, unicode)) | 136 | self.assertTrue(isinstance(skeleton.description, unicode)) |
155 | 141 | self.assertEqual(HASH1, skeleton.get_hash()) | 137 | self.assertEqual(HASH1, skeleton.get_hash()) |
156 | 142 | 138 | ||
157 | 139 | def test_build_skeleton_with_unicode_and_non_ascii(self): | ||
158 | 140 | """ | ||
159 | 141 | If with_unicode and with_info are passed to build_skeleton_apt, | ||
160 | 142 | the description is decoded and non-ascii chars replaced. | ||
161 | 143 | """ | ||
162 | 144 | self._add_package_to_deb_dir( | ||
163 | 145 | self.skeleton_repository_dir, "pkg", description=u"T\xe9st") | ||
164 | 146 | self.facade._cache.update(None) | ||
165 | 147 | self.facade._cache.open(None) | ||
166 | 148 | pkg = self.get_package("pkg") | ||
167 | 149 | skeleton = build_skeleton_apt(pkg, with_unicode=True, with_info=True) | ||
168 | 150 | self.assertEqual(u"T?st", skeleton.description) | ||
169 | 151 | |||
170 | 143 | def test_build_skeleton_minimal(self): | 152 | def test_build_skeleton_minimal(self): |
171 | 144 | """ | 153 | """ |
172 | 145 | A package that has only the required fields will still have some | 154 | A package that has only the required fields will still have some |
173 | 146 | relations defined. | 155 | relations defined. |
174 | 147 | """ | 156 | """ |
175 | 148 | minimal_package = self.get_package("minimal") | 157 | minimal_package = self.get_package("minimal") |
177 | 149 | skeleton = self.build_skeleton(minimal_package) | 158 | skeleton = build_skeleton_apt(minimal_package) |
178 | 150 | self.assertEqual("minimal", skeleton.name) | 159 | self.assertEqual("minimal", skeleton.name) |
179 | 151 | self.assertEqual("1.0", skeleton.version) | 160 | self.assertEqual("1.0", skeleton.version) |
180 | 152 | self.assertEqual(None, skeleton.section) | 161 | self.assertEqual(None, skeleton.section) |
181 | @@ -166,7 +175,7 @@ | |||
182 | 166 | be either an empty string or None, depending on which field. | 175 | be either an empty string or None, depending on which field. |
183 | 167 | """ | 176 | """ |
184 | 168 | package = self.get_package("minimal") | 177 | package = self.get_package("minimal") |
186 | 169 | skeleton = self.build_skeleton(package, True) | 178 | skeleton = build_skeleton_apt(package, True) |
187 | 170 | self.assertEqual("", skeleton.section) | 179 | self.assertEqual("", skeleton.section) |
188 | 171 | self.assertEqual( | 180 | self.assertEqual( |
189 | 172 | "A minimal package with no dependencies or other relations.", | 181 | "A minimal package with no dependencies or other relations.", |
190 | @@ -181,7 +190,7 @@ | |||
191 | 181 | simple, i.e. not specifying a version. | 190 | simple, i.e. not specifying a version. |
192 | 182 | """ | 191 | """ |
193 | 183 | package = self.get_package("simple-relations") | 192 | package = self.get_package("simple-relations") |
195 | 184 | skeleton = self.build_skeleton(package) | 193 | skeleton = build_skeleton_apt(package) |
196 | 185 | self.assertEqual("simple-relations", skeleton.name) | 194 | self.assertEqual("simple-relations", skeleton.name) |
197 | 186 | self.assertEqual("1.0", skeleton.version) | 195 | self.assertEqual("1.0", skeleton.version) |
198 | 187 | relations = [ | 196 | relations = [ |
199 | @@ -201,7 +210,7 @@ | |||
200 | 201 | version dependent. | 210 | version dependent. |
201 | 202 | """ | 211 | """ |
202 | 203 | package = self.get_package("version-relations") | 212 | package = self.get_package("version-relations") |
204 | 204 | skeleton = self.build_skeleton(package) | 213 | skeleton = build_skeleton_apt(package) |
205 | 205 | self.assertEqual("version-relations", skeleton.name) | 214 | self.assertEqual("version-relations", skeleton.name) |
206 | 206 | self.assertEqual("1.0", skeleton.version) | 215 | self.assertEqual("1.0", skeleton.version) |
207 | 207 | relations = [ | 216 | relations = [ |
208 | @@ -222,7 +231,7 @@ | |||
209 | 222 | skeleton. | 231 | skeleton. |
210 | 223 | """ | 232 | """ |
211 | 224 | package = self.get_package("multiple-relations") | 233 | package = self.get_package("multiple-relations") |
213 | 225 | skeleton = self.build_skeleton(package) | 234 | skeleton = build_skeleton_apt(package) |
214 | 226 | self.assertEqual("multiple-relations", skeleton.name) | 235 | self.assertEqual("multiple-relations", skeleton.name) |
215 | 227 | self.assertEqual("1.0", skeleton.version) | 236 | self.assertEqual("1.0", skeleton.version) |
216 | 228 | relations = [ | 237 | relations = [ |
217 | @@ -248,7 +257,7 @@ | |||
218 | 248 | is considered to be a single relation, with a special type. | 257 | is considered to be a single relation, with a special type. |
219 | 249 | """ | 258 | """ |
220 | 250 | package = self.get_package("or-relations") | 259 | package = self.get_package("or-relations") |
222 | 251 | skeleton = self.build_skeleton(package) | 260 | skeleton = build_skeleton_apt(package) |
223 | 252 | self.assertEqual("or-relations", skeleton.name) | 261 | self.assertEqual("or-relations", skeleton.name) |
224 | 253 | self.assertEqual("1.0", skeleton.version) | 262 | self.assertEqual("1.0", skeleton.version) |
225 | 254 | relations = [ | 263 | relations = [ |
226 | 255 | 264 | ||
227 | === modified file 'landscape/tests/helpers.py' | |||
228 | --- landscape/tests/helpers.py 2016-08-17 20:31:48 +0000 | |||
229 | +++ landscape/tests/helpers.py 2017-01-12 08:01:46 +0000 | |||
230 | @@ -94,7 +94,7 @@ | |||
231 | 94 | "%s" % (diff, extra)) | 94 | "%s" % (diff, extra)) |
232 | 95 | 95 | ||
233 | 96 | 96 | ||
235 | 97 | class LandscapeTest(MessageTestCase, HelperTestCase, TestCase, ): | 97 | class LandscapeTest(MessageTestCase, HelperTestCase, TestCase): |
236 | 98 | 98 | ||
237 | 99 | def setUp(self): | 99 | def setUp(self): |
238 | 100 | self._old_config_filenames = BaseConfiguration.default_config_filenames | 100 | self._old_config_filenames = BaseConfiguration.default_config_filenames |
Command: TRIAL_ARGS=-j4 make check /ci.lscape. net/job/ latch-test- precise/ 845/
Result: Success
Revno: 927
Branch: lp:~ack/landscape-client/package-description-decode-fix
Jenkins: https:/