Merge lp:~tealeg/landscape-client/warn-on-unicode into lp:~landscape/landscape-client/trunk
- warn-on-unicode
- Merge into trunk
Status: | Merged |
---|---|
Approved by: | Alberto Donato |
Approved revision: | 555 |
Merged at revision: | 547 |
Proposed branch: | lp:~tealeg/landscape-client/warn-on-unicode |
Merge into: | lp:~landscape/landscape-client/trunk |
Prerequisite: | lp:~tealeg/landscape-client/sanitise-hostname |
Diff against target: |
713 lines (+426/-76) 7 files modified
landscape/ui/view/configuration.py (+97/-26) landscape/ui/view/tests/test_configuration.py (+302/-31) man/landscape-client.1 (+1/-1) man/landscape-config.1 (+1/-1) man/landscape-message.1 (+1/-1) po/fr.po (+12/-8) po/landscape-client.pot (+12/-8) |
To merge this branch: | bzr merge lp:~tealeg/landscape-client/warn-on-unicode |
Related bugs: |
Reviewer | Review Type | Date Requested | Status |
---|---|---|---|
Alberto Donato (community) | Approve | ||
Mike Milner (community) | Approve | ||
Review via email: mp+99339@code.launchpad.net |
Commit message
Description of the change
Fixes bug #956612.
- Extends the InfoBar implementation from the sanitise-hostname branch to allow for more than one error to be displayed in parallel.
- Adds a check for non-ASCII input in Gtk.Entry fields.
- 552. By Geoff Teale
-
Restructure to avoid side effects and structure tests better (from milners review)
- 553. By Geoff Teale
-
merged forwards from trunk.
- 554. By Geoff Teale
-
lint
Geoff Teale (tealeg) wrote : | # |
> Looks good, but there are two aspects that should be looked at:
>
> [1]
> 35 - def is_valid_
> 36 - return HOSTNAME_
> 37 + def is_valid_
> 38 + host_name = self.sanitise_
>
> You're mixing concerns here. The is_valid_host_name (and is_ascii) functions
> now have side effects on the dialog. It would be better to keep the
> is_valid_host_name separate from the dialog-specific code. This makes the
> functions easy to test without worrying about the dialog side effects.
Very good point indeed - for some reason I couldn't see a sensible way around this the other day, but today it seems obvious. Anyhow, done.
>
>
> [2]
> 165 + def test_input_
> 166 + """
> 167 + Test that we can correctly identify non-ASCII input in a
> L{Gtk.Entry}.
> 168 + """
> 169 + dialog = ClientSettingsD
> 170 + self.run_
> 171 + dialog.
> 172 + self.run_
> 173 + dialog.
> 174 +
> self.assertTrue
> 175 + self.assertEqual(0, len(dialog.
> 176 + dialog.
> 177 +
> self.assertFals
> 178 + self.assertEqual(1, len(dialog.
>
> This test should be broken into two. Each test should have the minimum amount
> of code to verify one expected behaviour. Generally, tests should have the
> following structure: SETUP, RUN-CODE, ASSERT. If there are two groups of
> asserts there should probably be two tests. Same goes for
> test_dismiss_
I've done quite an extensive clean up here now. With specific regard to test_dismiss_
- 555. By Geoff Teale
-
Split test as per milner's review request.
Mike Milner (milner) wrote : | # |
> I've done quite an extensive clean up here now. With specific regard to
> test_dismiss_
> but I retain assertions halfway through those tests - that stems from my
> personal preference to assert that a condition exists in my test if I am
> testing a later action that changes that state. I can see that some people
> might think that pollutes the meaning of the test, but I find it ensures the
> validity of the result. Please advise if you find this distasteful.
Yeah checking preconditions makes sense, it was mainly about keeping the tests focused. If you're ever bored check out the S3 tests in boto for a great example of a monolithic super mega test and you'll see the bad extreme of this :)
Looks great! I especially like the Bob the Builder references ;) +1
Geoff Teale (tealeg) wrote : | # |
>
> Yeah checking preconditions makes sense, it was mainly about keeping the tests
> focused. If you're ever bored check out the S3 tests in boto for a great
> example of a monolithic super mega test and you'll see the bad extreme of this
> :)
I'll save that for a slow day ;-)
> Looks great! I especially like the Bob the Builder references ;) +1
Parenthood is pervasive.
Cheers for the review!
Alberto Donato (ack) wrote : | # |
Great! +1
#1:
+ else:
+ self.indicate_
+ if not host_name_ok:
+ self._validatio
+ if not ascii_ok:
+ self._validatio
+ return False
return False should be up one level of indentation (under the 'else' case)
- 556. By Geoff Teale
-
Correctly indent return. From ack's review.
Preview Diff
1 | === modified file 'landscape/ui/view/configuration.py' |
2 | --- landscape/ui/view/configuration.py 2012-03-28 08:32:37 +0000 |
3 | +++ landscape/ui/view/configuration.py 2012-03-28 14:18:17 +0000 |
4 | @@ -9,8 +9,34 @@ |
5 | CANONICAL_MANAGED, LOCAL_MANAGED, NOT_MANAGED) |
6 | |
7 | # Note, I think this may not be fully compliant with the changes in RFC 1123 |
8 | -HOSTNAME_REGEXP = re.compile("^(([a-zA-Z][a-zA-Z0-9\-]*)?[a-zA-Z0-9][\.]?)*" |
9 | - "(([A-Za-z][A-Za-z0-9\-]*)?[A-Za-z0-9])$") |
10 | +HOST_NAME_REGEXP = re.compile("^(([a-zA-Z][a-zA-Z0-9\-]*)?[a-zA-Z0-9][\.]?)*" |
11 | + "(([A-Za-z][A-Za-z0-9\-]*)?[A-Za-z0-9])$") |
12 | + |
13 | + |
14 | +def sanitise_host_name(host_name): |
15 | + """ |
16 | + Do some minimal host name sanitation. |
17 | + """ |
18 | + return host_name.strip() |
19 | + |
20 | + |
21 | +def is_valid_host_name(host_name): |
22 | + """ |
23 | + Check that the provided host name complies with L{HOST_NAME_REGEXP} and is |
24 | + therefor valid. |
25 | + """ |
26 | + return HOST_NAME_REGEXP.match(host_name) is not None |
27 | + |
28 | + |
29 | +def is_ascii(text): |
30 | + """ |
31 | + Test that the provided string contains only characters from the ASCII set. |
32 | + """ |
33 | + try: |
34 | + text.decode("ascii") |
35 | + return True |
36 | + except UnicodeDecodeError: |
37 | + return False |
38 | |
39 | |
40 | class ClientSettingsDialog(Gtk.Dialog): |
41 | @@ -21,6 +47,8 @@ |
42 | """ |
43 | |
44 | GLADE_FILE = "landscape-client-settings.glade" |
45 | + INVALID_HOST_NAME = 0 |
46 | + UNICODE_IN_ENTRY = 1 |
47 | |
48 | def __init__(self, controller): |
49 | super(ClientSettingsDialog, self).__init__( |
50 | @@ -29,37 +57,67 @@ |
51 | self.set_default_icon_name("preferences-management-service") |
52 | self.set_resizable(False) |
53 | self._initialised = False |
54 | + self._validation_errors = set() |
55 | + self._errored_entries = [] |
56 | self.controller = controller |
57 | self.setup_ui() |
58 | self.load_data() |
59 | # One extra revert to reset after loading data |
60 | self.controller.revert() |
61 | |
62 | - def sanitise_host_name(self, host_name): |
63 | - """ |
64 | - Do some minimal input sanitation. |
65 | - """ |
66 | - return host_name.strip() |
67 | - |
68 | - def is_valid_host_name(self, host_name): |
69 | - return HOSTNAME_REGEXP.match(host_name) is not None |
70 | + def indicate_error_on_entry(self, entry): |
71 | + """ |
72 | + Show a warning icon on a L{Gtk.Entry} to indicate some associated |
73 | + error. |
74 | + """ |
75 | + entry.set_icon_from_stock( |
76 | + Gtk.EntryIconPosition.PRIMARY, Gtk.STOCK_DIALOG_WARNING) |
77 | + self._errored_entries.append(entry) |
78 | + |
79 | + def check_local_landscape_host_name_entry(self): |
80 | + host_name = sanitise_host_name( |
81 | + self.local_landscape_host_entry.get_text()) |
82 | + ascii_ok = is_ascii(host_name) |
83 | + host_name_ok = is_valid_host_name(host_name) |
84 | + if ascii_ok and host_name_ok: |
85 | + self.local_landscape_host_entry.set_text(host_name) |
86 | + return True |
87 | + else: |
88 | + self.indicate_error_on_entry(self.local_landscape_host_entry) |
89 | + if not host_name_ok: |
90 | + self._validation_errors.add(self.INVALID_HOST_NAME) |
91 | + if not ascii_ok: |
92 | + self._validation_errors.add(self.UNICODE_IN_ENTRY) |
93 | + return False |
94 | + |
95 | + def check_entry(self, entry): |
96 | + """ |
97 | + Check that the text content of a L{Gtk.Entry} is acceptable. |
98 | + """ |
99 | + if is_ascii(entry.get_text()): |
100 | + return True |
101 | + else: |
102 | + self.indicate_error_on_entry(entry) |
103 | + self._validation_errors.add(self.UNICODE_IN_ENTRY) |
104 | + return False |
105 | |
106 | def validity_check(self): |
107 | - if self.use_type_combobox.get_active() < 2: |
108 | + self._validation_errors = set() |
109 | + if self._info_bar_container.get_visible(): |
110 | + self.dismiss_infobar(None) |
111 | + active_iter = self.liststore.get_iter( |
112 | + self.use_type_combobox.get_active()) |
113 | + [management_type] = self.liststore.get(active_iter, 0) |
114 | + if management_type == NOT_MANAGED: |
115 | return True |
116 | + elif management_type == CANONICAL_MANAGED: |
117 | + account_name_ok = self.check_entry(self.hosted_account_name_entry) |
118 | + password_ok = self.check_entry(self.hosted_password_entry) |
119 | + return account_name_ok and password_ok |
120 | else: |
121 | - host_name = self.sanitise_host_name( |
122 | - self.local_landscape_host_entry.get_text()) |
123 | - if self.is_valid_host_name(host_name): |
124 | - self.local_landscape_host_entry.set_text(host_name) |
125 | - return True |
126 | - else: |
127 | - self.info_message.set_text(self.INVALID_HOSTNAME_MESSAGE) |
128 | - self.local_landscape_host_entry.set_icon_from_stock( |
129 | - Gtk.EntryIconPosition.PRIMARY, |
130 | - Gtk.STOCK_DIALOG_WARNING) |
131 | - self._info_bar_container.show() |
132 | - return False |
133 | + host_name_ok = self.check_local_landscape_host_name_entry() |
134 | + password_ok = self.check_entry(self.local_password_entry) |
135 | + return host_name_ok and password_ok |
136 | |
137 | @property |
138 | def NO_SERVICE_TEXT(self): |
139 | @@ -82,9 +140,13 @@ |
140 | return _("Disable") |
141 | |
142 | @property |
143 | - def INVALID_HOSTNAME_MESSAGE(self): |
144 | + def INVALID_HOST_NAME_MESSAGE(self): |
145 | return _("Invalid host name.") |
146 | |
147 | + @property |
148 | + def UNICODE_IN_ENTRY_MESSAGE(self): |
149 | + return _("Only ASCII characters are allowed.") |
150 | + |
151 | def _set_use_type_combobox_from_controller(self): |
152 | """ |
153 | Load the persisted L{management_type} from the controller and set the |
154 | @@ -177,6 +239,14 @@ |
155 | def register_response(self, widget): |
156 | if self.validity_check(): |
157 | self.response(Gtk.ResponseType.OK) |
158 | + else: |
159 | + error_text = [] |
160 | + if self.UNICODE_IN_ENTRY in self._validation_errors: |
161 | + error_text.append(self.UNICODE_IN_ENTRY_MESSAGE) |
162 | + if self.INVALID_HOST_NAME in self._validation_errors: |
163 | + error_text.append(self.INVALID_HOST_NAME_MESSAGE) |
164 | + self.info_message.set_text("\n".join(error_text)) |
165 | + self._info_bar_container.show() |
166 | |
167 | def set_button_text(self, management_type): |
168 | [alignment] = self.register_button.get_children() |
169 | @@ -203,8 +273,9 @@ |
170 | |
171 | def dismiss_infobar(self, widget): |
172 | self._info_bar_container.hide() |
173 | - self.local_landscape_host_entry.set_icon_from_stock( |
174 | - Gtk.EntryIconPosition.PRIMARY, None) |
175 | + for entry in self._errored_entries: |
176 | + entry.set_icon_from_stock(Gtk.EntryIconPosition.PRIMARY, None) |
177 | + self._errored_entries = [] |
178 | |
179 | def setup_info_bar(self): |
180 | labels_size_group = self._builder.get_object("labels-sizegroup") |
181 | |
182 | === modified file 'landscape/ui/view/tests/test_configuration.py' |
183 | --- landscape/ui/view/tests/test_configuration.py 2012-03-28 08:18:47 +0000 |
184 | +++ landscape/ui/view/tests/test_configuration.py 2012-03-28 14:18:17 +0000 |
185 | @@ -6,7 +6,8 @@ |
186 | |
187 | if got_gobject_introspection: |
188 | from gi.repository import Gtk |
189 | - from landscape.ui.view.configuration import ClientSettingsDialog |
190 | + from landscape.ui.view.configuration import ( |
191 | + ClientSettingsDialog, sanitise_host_name, is_valid_host_name) |
192 | from landscape.ui.controller.configuration import ConfigController |
193 | import landscape.ui.model.configuration.state |
194 | from landscape.ui.model.configuration.state import ( |
195 | @@ -16,6 +17,47 @@ |
196 | from landscape.tests.helpers import LandscapeTest |
197 | |
198 | |
199 | +class ViewFunctionsTest(LandscapeTest): |
200 | + |
201 | + def test_sanitise_host_name(self): |
202 | + """ |
203 | + Test UI level host_name sanitation. |
204 | + """ |
205 | + self.assertEqual("foo.bar", sanitise_host_name(" foo.bar")) |
206 | + self.assertEqual("foo.bar", sanitise_host_name("foo.bar ")) |
207 | + self.assertEqual("foo.bar", sanitise_host_name(" foo.bar ")) |
208 | + |
209 | + def test_is_valid_host_name_ok(self): |
210 | + """ |
211 | + Test that valid host names cause L{is_valid_host_name} to return |
212 | + L{True}. |
213 | + """ |
214 | + self.assertTrue(is_valid_host_name("a")) |
215 | + self.assertTrue(is_valid_host_name("a.b")) |
216 | + self.assertTrue(is_valid_host_name("a.b.c")) |
217 | + self.assertTrue(is_valid_host_name("stop-squark")) |
218 | + self.assertTrue(is_valid_host_name("stop-squark.teale.DE")) |
219 | + self.assertTrue(is_valid_host_name("a2.b3.c4")) |
220 | + |
221 | + def test_is_valid_host_name_bad(self): |
222 | + """ |
223 | + Test that invalid host names cause L{is_valid_host_name} to return |
224 | + L{False}. |
225 | + """ |
226 | + self.assertFalse(is_valid_host_name(".a")) |
227 | + self.assertFalse(is_valid_host_name("a.")) |
228 | + self.assertFalse(is_valid_host_name("a b")) |
229 | + self.assertFalse(is_valid_host_name("a .b")) |
230 | + self.assertFalse(is_valid_host_name("a. b")) |
231 | + |
232 | + def test_is_valid_host_name_unicode(self): |
233 | + """ |
234 | + Test that host names containing Unicode cause L{is_valid_host_name} to |
235 | + return L{False}. |
236 | + """ |
237 | + self.assertFalse(is_valid_host_name(u"\xc3a")) |
238 | + |
239 | + |
240 | class ConfigurationViewTest(LandscapeTest): |
241 | |
242 | helpers = [ConfigurationProxyHelper] |
243 | @@ -210,58 +252,287 @@ |
244 | self.assertEqual("foo", dialog.hosted_account_name_entry.get_text()) |
245 | self.assertEqual("bar", dialog.hosted_password_entry.get_text()) |
246 | |
247 | - def test_sanitise_host_name(self): |
248 | - """ |
249 | - Test that L{ClientSettingsDialog.sanitise_host_name} removes both |
250 | - leading and trailing white space from a host name. |
251 | - """ |
252 | - dialog = ClientSettingsDialog(self.controller) |
253 | - self.assertEquals("foo.bar", dialog.sanitise_host_name(" foo.bar")) |
254 | - self.assertEquals("foo.bar", dialog.sanitise_host_name("foo.bar ")) |
255 | - self.assertEquals("foo.bar", dialog.sanitise_host_name(" foo.bar ")) |
256 | - |
257 | - def test_is_valid_host_name(self): |
258 | - """ |
259 | - Test that L{is_valid_host_name} checks host names correctly. |
260 | - """ |
261 | - dialog = ClientSettingsDialog(self.controller) |
262 | - self.assertTrue(dialog.is_valid_host_name("foo.bar")) |
263 | - self.assertFalse(dialog.is_valid_host_name("foo bar")) |
264 | - self.assertFalse(dialog.is_valid_host_name("f\xc3.bar")) |
265 | - |
266 | - def test_validity_check(self): |
267 | - """ |
268 | - Test that the L{validity_check} returns True for valid input and False |
269 | - for invalid input. |
270 | - """ |
271 | - dialog = ClientSettingsDialog(self.controller) |
272 | - self.run_gtk_eventloop() |
273 | - |
274 | - # Checking disable should always return True |
275 | + def test_check_local_landscape_host_name_entry_ok(self): |
276 | + """ |
277 | + Test that L{check_local_landscape_host_name_entry} returns L{True} when |
278 | + the input is a valid host name. |
279 | + """ |
280 | + dialog = ClientSettingsDialog(self.controller) |
281 | + dialog.use_type_combobox.set_active(2) |
282 | + dialog.local_landscape_host_entry.set_text("foo.bar") |
283 | + self.assertTrue(dialog.check_local_landscape_host_name_entry()) |
284 | + |
285 | + def test_check_local_landscape_host_name_entry_ok_not_recorded(self): |
286 | + """ |
287 | + Test that L{check_local_landscape_host_name_entry} does not add the |
288 | + entry to L{ClientSettingsDialog._errored_entries} when the input is a |
289 | + valid host name. |
290 | + """ |
291 | + dialog = ClientSettingsDialog(self.controller) |
292 | + dialog.use_type_combobox.set_active(2) |
293 | + dialog.local_landscape_host_entry.set_text("foo.bar") |
294 | + dialog.check_local_landscape_host_name_entry() |
295 | + self.assertEqual(0, len(dialog._errored_entries)) |
296 | + |
297 | + def test_check_local_landscape_host_name_entry_bad_host_name(self): |
298 | + """ |
299 | + Test that L{check_local_landscape_host_name_entry} returns L{False} |
300 | + when the input is not a valid host name. |
301 | + """ |
302 | + dialog = ClientSettingsDialog(self.controller) |
303 | + dialog.use_type_combobox.set_active(2) |
304 | + dialog.local_landscape_host_entry.set_text("foo bar") |
305 | + self.assertFalse(dialog.check_local_landscape_host_name_entry()) |
306 | + |
307 | + def test_check_local_landscape_host_name_entry_bad_recorded(self): |
308 | + """ |
309 | + Test that L{check_local_landscape_host_name_entry} does add the |
310 | + entry to L{ClientSettingsDialog._errored_entries} when the input is not |
311 | + a valid host name. |
312 | + """ |
313 | + dialog = ClientSettingsDialog(self.controller) |
314 | + dialog.use_type_combobox.set_active(2) |
315 | + dialog.local_landscape_host_entry.set_text("foo bar") |
316 | + dialog.check_local_landscape_host_name_entry() |
317 | + self.assertEqual(1, len(dialog._errored_entries)) |
318 | + |
319 | + def test_check_local_landscape_host_name_entry_bad_error_type(self): |
320 | + """ |
321 | + Test that L{check_local_landscape_host_name_entry} adds the correct |
322 | + error type to L{ClientSettingsDialog._validation_errors} when the input |
323 | + is not a valid host name. |
324 | + """ |
325 | + dialog = ClientSettingsDialog(self.controller) |
326 | + dialog.use_type_combobox.set_active(2) |
327 | + dialog.local_landscape_host_entry.set_text("foo bar") |
328 | + dialog.check_local_landscape_host_name_entry() |
329 | + self.assertEqual(set([dialog.INVALID_HOST_NAME]), |
330 | + dialog._validation_errors) |
331 | + |
332 | + def test_check_local_landscape_host_name_entry_unicode_in_host_name(self): |
333 | + """ |
334 | + Test that L{check_local_landscape_host_name_entry} returns L{False} |
335 | + when the input contains Unicode. |
336 | + """ |
337 | + dialog = ClientSettingsDialog(self.controller) |
338 | + dialog.use_type_combobox.set_active(2) |
339 | + dialog.local_landscape_host_entry.set_text(u"f\xc3.bar") |
340 | + self.assertFalse(dialog.check_local_landscape_host_name_entry()) |
341 | + |
342 | + def test_check_local_landscape_host_name_entry_unicode_recorded(self): |
343 | + """ |
344 | + Test that L{check_local_landscape_host_name_entry} does add the |
345 | + entry to L{ClientSettingsDialog._errored_entries} when the input |
346 | + contains Unicode. |
347 | + """ |
348 | + dialog = ClientSettingsDialog(self.controller) |
349 | + dialog.use_type_combobox.set_active(2) |
350 | + dialog.local_landscape_host_entry.set_text(u"f\xc3.bar") |
351 | + dialog.check_local_landscape_host_name_entry() |
352 | + self.assertEqual(1, len(dialog._errored_entries)) |
353 | + |
354 | + def test_check_local_landscape_host_name_entry_unicode_error_type(self): |
355 | + """ |
356 | + Test that L{check_local_landscape_host_name_entry} adds the correct |
357 | + error type to L{ClientSettingsDialog._validation_errors} when the input |
358 | + contains Unicode. |
359 | + """ |
360 | + dialog = ClientSettingsDialog(self.controller) |
361 | + dialog.use_type_combobox.set_active(2) |
362 | + dialog.local_landscape_host_entry.set_text(u"f\xc3.bar") |
363 | + dialog.check_local_landscape_host_name_entry() |
364 | + self.assertEqual( |
365 | + set([dialog.INVALID_HOST_NAME, dialog.UNICODE_IN_ENTRY]), |
366 | + dialog._validation_errors) |
367 | + |
368 | + def test_check_entry_ok(self): |
369 | + """ |
370 | + Test that we return L{True} when the text of a L{Gtk.Entry} is valid |
371 | + input. |
372 | + """ |
373 | + dialog = ClientSettingsDialog(self.controller) |
374 | + self.run_gtk_eventloop() |
375 | + dialog.use_type_combobox.set_active(1) |
376 | + self.run_gtk_eventloop() |
377 | + dialog.hosted_account_name_entry.set_text("Toodleoo") |
378 | + self.assertTrue(dialog.check_entry(dialog.hosted_account_name_entry)) |
379 | + |
380 | + def test_check_entry_doesnt_record_entry_when_ok(self): |
381 | + """ |
382 | + Test that, when the text of a L{Gtk.Entry} is valid nothing is added to |
383 | + L{ClientSettingsDialog._errored_entries}. |
384 | + """ |
385 | + dialog = ClientSettingsDialog(self.controller) |
386 | + self.run_gtk_eventloop() |
387 | + dialog.use_type_combobox.set_active(1) |
388 | + self.run_gtk_eventloop() |
389 | + dialog.hosted_account_name_entry.set_text("Toodleoo") |
390 | + dialog.check_entry(dialog.hosted_account_name_entry) |
391 | + self.assertEqual(0, len(dialog._errored_entries)) |
392 | + |
393 | + def test_check_entry_non_ascii(self): |
394 | + """ |
395 | + Test that we return L{False} when the text of a L{Gtk.Entry} contains |
396 | + Unicode input. |
397 | + """ |
398 | + dialog = ClientSettingsDialog(self.controller) |
399 | + self.run_gtk_eventloop() |
400 | + dialog.use_type_combobox.set_active(1) |
401 | + self.run_gtk_eventloop() |
402 | + dialog.hosted_account_name_entry.set_text(u"T\xc3dle\xc4") |
403 | + self.assertFalse(dialog.check_entry(dialog.hosted_account_name_entry)) |
404 | + |
405 | + def test_check_entry_records_entry_when_non_ascii(self): |
406 | + """ |
407 | + Test that, when the text of a L{Gtk.Entry} contains Unicode it is |
408 | + added to L{ClientSettingsDialog._errored_entries}. |
409 | + """ |
410 | + dialog = ClientSettingsDialog(self.controller) |
411 | + self.run_gtk_eventloop() |
412 | + dialog.use_type_combobox.set_active(1) |
413 | + self.run_gtk_eventloop() |
414 | + dialog.hosted_account_name_entry.set_text(u"T\xc3dle\xc4") |
415 | + dialog.check_entry(dialog.hosted_account_name_entry) |
416 | + self.assertEqual(1, len(dialog._errored_entries)) |
417 | + |
418 | + def test_dismiss_validation_errors_local(self): |
419 | + """ |
420 | + Test that dismissing the validation errors tidies up indicators that |
421 | + have been set against local settings. |
422 | + """ |
423 | + dialog = ClientSettingsDialog(self.controller) |
424 | + self.run_gtk_eventloop() |
425 | + dialog.use_type_combobox.set_active(1) |
426 | + self.run_gtk_eventloop() |
427 | + dialog.hosted_account_name_entry.set_text(u"T\xc3dle\xc4") |
428 | + dialog.hosted_password_entry.set_text(u"T\xc3dle\xc4") |
429 | + self.run_gtk_eventloop() |
430 | + dialog.validity_check() |
431 | + self.run_gtk_eventloop() |
432 | + self.assertEqual(2, len(dialog._errored_entries)) |
433 | + [entry1, entry2] = dialog._errored_entries |
434 | + self.assertEqual(Gtk.STOCK_DIALOG_WARNING, |
435 | + entry1.get_icon_stock(Gtk.EntryIconPosition.PRIMARY)) |
436 | + self.assertEqual(Gtk.STOCK_DIALOG_WARNING, |
437 | + entry2.get_icon_stock(Gtk.EntryIconPosition.PRIMARY)) |
438 | + dialog.dismiss_infobar(None) |
439 | + self.run_gtk_eventloop() |
440 | + self.assertEqual(0, len(dialog._errored_entries)) |
441 | + self.assertNotEqual( |
442 | + Gtk.STOCK_DIALOG_WARNING, |
443 | + entry1.get_icon_stock(Gtk.EntryIconPosition.PRIMARY)) |
444 | + self.assertNotEqual( |
445 | + Gtk.STOCK_DIALOG_WARNING, |
446 | + entry2.get_icon_stock(Gtk.EntryIconPosition.PRIMARY)) |
447 | + |
448 | + def test_dismiss_validation_errors_hosted(self): |
449 | + """ |
450 | + Test that dismissing the validation errors tidies up indicators that |
451 | + have been set against hosted fields. |
452 | + """ |
453 | + dialog = ClientSettingsDialog(self.controller) |
454 | + self.run_gtk_eventloop() |
455 | + dialog.use_type_combobox.set_active(2) |
456 | + self.run_gtk_eventloop() |
457 | + dialog.local_landscape_host_entry.set_text("dodgy as hell") |
458 | + self.run_gtk_eventloop() |
459 | + dialog.validity_check() |
460 | + self.run_gtk_eventloop() |
461 | + self.assertEqual(1, len(dialog._errored_entries)) |
462 | + [entry1] = dialog._errored_entries |
463 | + self.assertEqual(Gtk.STOCK_DIALOG_WARNING, |
464 | + entry1.get_icon_stock(Gtk.EntryIconPosition.PRIMARY)) |
465 | + dialog.dismiss_infobar(None) |
466 | + self.run_gtk_eventloop() |
467 | + self.assertEqual(0, len(dialog._errored_entries)) |
468 | + self.assertNotEqual( |
469 | + Gtk.STOCK_DIALOG_WARNING, |
470 | + entry1.get_icon_stock(Gtk.EntryIconPosition.PRIMARY)) |
471 | + |
472 | + def test_validity_check_disabled(self): |
473 | + """ |
474 | + Test that the L{validity_check} returns True when we disable landscape |
475 | + client. |
476 | + """ |
477 | + dialog = ClientSettingsDialog(self.controller) |
478 | + self.run_gtk_eventloop() |
479 | dialog.use_type_combobox.set_active(0) |
480 | self.run_gtk_eventloop() |
481 | self.assertTrue(dialog.validity_check()) |
482 | |
483 | - # Check for hosted - currently returns True always |
484 | + def test_validity_check_hosted(self): |
485 | + """ |
486 | + Test that the L{validity_check} returns True when the hosted fields are |
487 | + valid. |
488 | + """ |
489 | + dialog = ClientSettingsDialog(self.controller) |
490 | + self.run_gtk_eventloop() |
491 | dialog.use_type_combobox.set_active(1) |
492 | + dialog.hosted_account_name_entry.set_text("Bob") |
493 | + dialog.hosted_password_entry.set_text("the builder") |
494 | self.run_gtk_eventloop() |
495 | self.assertTrue(dialog.validity_check()) |
496 | |
497 | - # Checks for local |
498 | + def test_validity_check_hosted_unicode(self): |
499 | + """ |
500 | + Test that the L{validity_check} returns False when the hosted fields |
501 | + contain Unicode. |
502 | + """ |
503 | + dialog = ClientSettingsDialog(self.controller) |
504 | + self.run_gtk_eventloop() |
505 | + dialog.use_type_combobox.set_active(1) |
506 | + dialog.hosted_account_name_entry.set_text(u"B\xc3b") |
507 | + self.run_gtk_eventloop() |
508 | + self.assertFalse(dialog.validity_check()) |
509 | + |
510 | + def test_validity_check_local_ok(self): |
511 | + """ |
512 | + Test that the L{validity_check} returns True when the local fields |
513 | + are valid. |
514 | + """ |
515 | + dialog = ClientSettingsDialog(self.controller) |
516 | + self.run_gtk_eventloop() |
517 | dialog.use_type_combobox.set_active(2) |
518 | self.run_gtk_eventloop() |
519 | dialog.local_landscape_host_entry.set_text("foo.bar") |
520 | self.run_gtk_eventloop() |
521 | self.assertTrue(dialog.validity_check()) |
522 | + |
523 | + def test_validity_check_local_sanitisable(self): |
524 | + """ |
525 | + Test that the L{validity_check} returns True when the local fields |
526 | + are valid after sanitation. |
527 | + """ |
528 | + dialog = ClientSettingsDialog(self.controller) |
529 | + self.run_gtk_eventloop() |
530 | + dialog.use_type_combobox.set_active(2) |
531 | dialog.local_landscape_host_entry.set_text(" foo.bar") |
532 | self.run_gtk_eventloop() |
533 | self.assertTrue(dialog.validity_check()) |
534 | dialog.local_landscape_host_entry.set_text("foo.bar ") |
535 | self.run_gtk_eventloop() |
536 | self.assertTrue(dialog.validity_check()) |
537 | + |
538 | + def test_validity_check_local_invalid_host_name(self): |
539 | + """ |
540 | + Test that the L{validity_check} returns False when the host name is |
541 | + invalid. |
542 | + """ |
543 | + dialog = ClientSettingsDialog(self.controller) |
544 | + self.run_gtk_eventloop() |
545 | + dialog.use_type_combobox.set_active(2) |
546 | dialog.local_landscape_host_entry.set_text("foo bar") |
547 | self.run_gtk_eventloop() |
548 | self.assertFalse(dialog.validity_check()) |
549 | + |
550 | + def test_validity_check_local_unicode(self): |
551 | + """ |
552 | + Test that the L{validity_check} returns False when the host name |
553 | + contains Unicode. |
554 | + """ |
555 | + dialog = ClientSettingsDialog(self.controller) |
556 | + self.run_gtk_eventloop() |
557 | + dialog.use_type_combobox.set_active(2) |
558 | dialog.local_landscape_host_entry.set_text(u"f\xc3.bar") |
559 | self.run_gtk_eventloop() |
560 | self.assertFalse(dialog.validity_check()) |
561 | |
562 | === modified file 'man/landscape-client.1' |
563 | --- man/landscape-client.1 2012-03-28 08:38:34 +0000 |
564 | +++ man/landscape-client.1 2012-03-28 14:18:17 +0000 |
565 | @@ -1,5 +1,5 @@ |
566 | .\"Text automatically generated by txt2man |
567 | -.TH landscape-client "18 January 2010" "" "" |
568 | +.TH landscape-client 1 "27 March 2012" "" "" |
569 | .SH NAME |
570 | \fBlandscape-client \fP- Landscape system client |
571 | \fB |
572 | |
573 | === modified file 'man/landscape-config.1' |
574 | --- man/landscape-config.1 2012-03-28 08:38:34 +0000 |
575 | +++ man/landscape-config.1 2012-03-28 14:18:17 +0000 |
576 | @@ -1,5 +1,5 @@ |
577 | .\"Text automatically generated by txt2man |
578 | -.TH landscape-config "18 January 2010" "" "" |
579 | +.TH landscape-config 1 "27 March 2012" "" "" |
580 | .SH NAME |
581 | \fBlandscape-config \fP- configure the Landscape management client |
582 | \fB |
583 | |
584 | === modified file 'man/landscape-message.1' |
585 | --- man/landscape-message.1 2012-03-28 08:38:34 +0000 |
586 | +++ man/landscape-message.1 2012-03-28 14:18:17 +0000 |
587 | @@ -1,5 +1,5 @@ |
588 | .\"Text automatically generated by txt2man |
589 | -.TH landscape-message "18 January 2010" "" "" |
590 | +.TH landscape-message 1 "27 March 2012" "" "" |
591 | .SH NAME |
592 | \fBlandscape-message \fP- Send a message to the landscape web interface |
593 | \fB |
594 | |
595 | === modified file 'po/fr.po' |
596 | --- po/fr.po 2012-03-28 08:38:34 +0000 |
597 | +++ po/fr.po 2012-03-28 14:18:17 +0000 |
598 | @@ -8,7 +8,7 @@ |
599 | msgstr "" |
600 | "Project-Id-Version: PACKAGE VERSION\n" |
601 | "Report-Msgid-Bugs-To: \n" |
602 | -"POT-Creation-Date: 2012-03-28 10:34+0200\n" |
603 | +"POT-Creation-Date: 2012-03-28 11:07+0200\n" |
604 | "PO-Revision-Date: 2012-03-23 11:25+0100\n" |
605 | "Last-Translator: Thomas Hervé <thomas.herve@canonical.com>\n" |
606 | "Language-Team: français <>\n" |
607 | @@ -56,35 +56,39 @@ |
608 | msgid "Attempting to disable landscape client." |
609 | msgstr "Tentative de désactivation du client Landscape." |
610 | |
611 | -#: ../landscape/ui/view/configuration.py:27 |
612 | +#: ../landscape/ui/view/configuration.py:55 |
613 | #: ../applications/landscape-client-settings.desktop.in.h:1 |
614 | msgid "Management Service" |
615 | msgstr "Service de gestion" |
616 | |
617 | -#: ../landscape/ui/view/configuration.py:66 |
618 | +#: ../landscape/ui/view/configuration.py:124 |
619 | msgid "None" |
620 | msgstr "Aucun" |
621 | |
622 | -#: ../landscape/ui/view/configuration.py:70 |
623 | +#: ../landscape/ui/view/configuration.py:128 |
624 | msgid "Landscape - hosted by Canonical" |
625 | msgstr "Landscape - hébergé par Canonical" |
626 | |
627 | -#: ../landscape/ui/view/configuration.py:74 |
628 | +#: ../landscape/ui/view/configuration.py:132 |
629 | msgid "Landscape - dedicated server" |
630 | msgstr "Landscape - serveur dédié" |
631 | |
632 | -#: ../landscape/ui/view/configuration.py:78 |
633 | +#: ../landscape/ui/view/configuration.py:136 |
634 | msgid "Register" |
635 | msgstr "S'enregistrer" |
636 | |
637 | -#: ../landscape/ui/view/configuration.py:82 |
638 | +#: ../landscape/ui/view/configuration.py:140 |
639 | msgid "Disable" |
640 | msgstr "Désactiver" |
641 | |
642 | -#: ../landscape/ui/view/configuration.py:86 |
643 | +#: ../landscape/ui/view/configuration.py:144 |
644 | msgid "Invalid host name." |
645 | msgstr "Nom d'hôte invalide." |
646 | |
647 | +#: ../landscape/ui/view/configuration.py:148 |
648 | +msgid "Only ASCII characters are allowed." |
649 | +msgstr "Seuls les caractères ASCII sont autritorisés." |
650 | + |
651 | #: ../landscape/ui/view/ui/landscape-client-settings.glade.h:1 |
652 | msgid "" |
653 | "Landscape is a remote administration service from Canonical. If you allow " |
654 | |
655 | === modified file 'po/landscape-client.pot' |
656 | --- po/landscape-client.pot 2012-03-28 08:38:34 +0000 |
657 | +++ po/landscape-client.pot 2012-03-28 14:18:17 +0000 |
658 | @@ -8,7 +8,7 @@ |
659 | msgstr "" |
660 | "Project-Id-Version: PACKAGE VERSION\n" |
661 | "Report-Msgid-Bugs-To: \n" |
662 | -"POT-Creation-Date: 2012-03-28 10:34+0200\n" |
663 | +"POT-Creation-Date: 2012-03-28 11:07+0200\n" |
664 | "PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n" |
665 | "Last-Translator: FULL NAME <EMAIL@ADDRESS>\n" |
666 | "Language-Team: LANGUAGE <LL@li.org>\n" |
667 | @@ -55,35 +55,39 @@ |
668 | msgid "Attempting to disable landscape client." |
669 | msgstr "" |
670 | |
671 | -#: ../landscape/ui/view/configuration.py:27 |
672 | +#: ../landscape/ui/view/configuration.py:55 |
673 | #: ../applications/landscape-client-settings.desktop.in.h:1 |
674 | msgid "Management Service" |
675 | msgstr "" |
676 | |
677 | -#: ../landscape/ui/view/configuration.py:66 |
678 | +#: ../landscape/ui/view/configuration.py:124 |
679 | msgid "None" |
680 | msgstr "" |
681 | |
682 | -#: ../landscape/ui/view/configuration.py:70 |
683 | +#: ../landscape/ui/view/configuration.py:128 |
684 | msgid "Landscape - hosted by Canonical" |
685 | msgstr "" |
686 | |
687 | -#: ../landscape/ui/view/configuration.py:74 |
688 | +#: ../landscape/ui/view/configuration.py:132 |
689 | msgid "Landscape - dedicated server" |
690 | msgstr "" |
691 | |
692 | -#: ../landscape/ui/view/configuration.py:78 |
693 | +#: ../landscape/ui/view/configuration.py:136 |
694 | msgid "Register" |
695 | msgstr "" |
696 | |
697 | -#: ../landscape/ui/view/configuration.py:82 |
698 | +#: ../landscape/ui/view/configuration.py:140 |
699 | msgid "Disable" |
700 | msgstr "" |
701 | |
702 | -#: ../landscape/ui/view/configuration.py:86 |
703 | +#: ../landscape/ui/view/configuration.py:144 |
704 | msgid "Invalid host name." |
705 | msgstr "" |
706 | |
707 | +#: ../landscape/ui/view/configuration.py:148 |
708 | +msgid "Only ASCII characters are allowed." |
709 | +msgstr "" |
710 | + |
711 | #: ../landscape/ui/view/ui/landscape-client-settings.glade.h:1 |
712 | msgid "" |
713 | "Landscape is a remote administration service from Canonical. If you allow " |
Looks good, but there are two aspects that should be looked at:
[1] host_name( self, host_name): REGEXP. match(host_ name) is not None host_name( self, entry): host_name( entry.get_ text())
35 - def is_valid_
36 - return HOSTNAME_
37 + def is_valid_
38 + host_name = self.sanitise_
You're mixing concerns here. The is_valid_host_name (and is_ascii) functions now have side effects on the dialog. It would be better to keep the is_valid_host_name separate from the dialog-specific code. This makes the functions easy to test without worrying about the dialog side effects.
[2] is_ascii( self): ialog(self. controller) gtk_eventloop( ) use_type_ combobox. set_active( 1) gtk_eventloop( ) hosted_ account_ name_entry. set_text( "Toodleoo" ) (dialog. is_ascii( dialog. hosted_ account_ name_entry) ) _errored_ entries) ) hosted_ account_ name_entry. set_text( u"T\xc3dle\ xc4") e(dialog. is_ascii( dialog. hosted_ account_ name_entry) ) _errored_ entries) )
165 + def test_input_
166 + """
167 + Test that we can correctly identify non-ASCII input in a L{Gtk.Entry}.
168 + """
169 + dialog = ClientSettingsD
170 + self.run_
171 + dialog.
172 + self.run_
173 + dialog.
174 + self.assertTrue
175 + self.assertEqual(0, len(dialog.
176 + dialog.
177 + self.assertFals
178 + self.assertEqual(1, len(dialog.
This test should be broken into two. Each test should have the minimum amount of code to verify one expected behaviour. Generally, tests should have the following structure: SETUP, RUN-CODE, ASSERT. If there are two groups of asserts there should probably be two tests. Same goes for test_dismiss_ validation_ errors.