Merge lp://qastaging/~rick-rickspencer3/desktopcouch/CouchWidget-tests-and-robustness into lp://qastaging/desktopcouch
- CouchWidget-tests-and-robustness
- Merge into trunk
Status: | Merged | ||||
---|---|---|---|---|---|
Approved by: | Rodrigo Moya | ||||
Approved revision: | 44 | ||||
Merged at revision: | not available | ||||
Proposed branch: | lp://qastaging/~rick-rickspencer3/desktopcouch/CouchWidget-tests-and-robustness | ||||
Merge into: | lp://qastaging/desktopcouch | ||||
Diff against target: | None lines | ||||
To merge this branch: | bzr merge lp://qastaging/~rick-rickspencer3/desktopcouch/CouchWidget-tests-and-robustness | ||||
Related bugs: |
|
Reviewer | Review Type | Date Requested | Status |
---|---|---|---|
Rodrigo Moya (community) | Approve | ||
Eric Casteleijn (community) | Approve | ||
Review via email:
|
Commit message
This commit is focused on couchwidget.py. It fixes some bugs, and adds tests for CouchWidget. It also modifies contactspicker.py to use the desktopcouch api and to not break with the CouchWidget changes.
Description of the change
![](/+icing/build/overlay/assets/skins/sam/images/close.gif)
Rick Spencer (rick-rickspencer3) wrote : | # |
![](/+icing/build/overlay/assets/skins/sam/images/close.gif)
Rick Spencer (rick-rickspencer3) wrote : | # |
Note that this branch breaks the CouchWidget API by requiring a database be specified when the widget is created.
This branch fixes bug 412266.
This branch adds more tests for CouchWidget, and improves the tests that existed.
- 42. By Rick Spencer
-
modified contactspicker.py to accomdate api changes and also to not use static local ip address
- 43. By Rick Spencer
-
added nicer titles to contactspicker.py
- 44. By Rick Spencer
-
removed now eroneous doc comment, and made contactspicker titles look a little nicer
![](/+icing/build/overlay/assets/skins/sam/images/close.gif)
Eric Casteleijn (thisfred) wrote : | # |
The code looks good and passes tests, but I see a lot of PEP-8 violations.
Specifically: long lines (max 79 chars + /n), and missing spaces after punctuation like colons in dicts and commas.
![](/+icing/build/overlay/assets/skins/sam/images/close.gif)
Rodrigo Moya (rodrigo-moya) wrote : | # |
> Note that this branch breaks the CouchWidget API by requiring a database be
> specified when the widget is created.
>
please change then the contactspicker.py CouchWidget creation, if not it will break when you merge this branch
![](/+icing/build/overlay/assets/skins/sam/images/close.gif)
Rick Spencer (rick-rickspencer3) wrote : | # |
> > Note that this branch breaks the CouchWidget API by requiring a database be
> > specified when the widget is created.
> >
> please change then the contactspicker.py CouchWidget creation, if not it will
> break when you merge this branch
I did so in commit #43. I fixed a couple of other issues there as well. Perhaps I didn't do the merge proposal correctly, as I pushed those changes *after* making the merge proposal. Sorry about that.
Cheers, Rick
![](/+icing/build/overlay/assets/skins/sam/images/close.gif)
Rodrigo Moya (rodrigo-moya) : | # |
Preview Diff
1 | === modified file 'desktopcouch/records/couchwidget.py' |
2 | --- desktopcouch/records/couchwidget.py 2009-08-19 23:01:09 +0000 |
3 | +++ desktopcouch/records/couchwidget.py 2009-08-20 21:32:18 +0000 |
4 | @@ -26,24 +26,46 @@ |
5 | class CouchWidget(gtk.TreeView): |
6 | __gtype_name__ = "CouchWidget" |
7 | |
8 | - def __init__(self, server_ip = 'http://127.0.0.1:5984/'): |
9 | + def __init__(self, database_name, record_type = None, headings = None): |
10 | """Create a new Couchwidget |
11 | + arguments: |
12 | + database_name - specify the name of the database in the desktop |
13 | + couchdb to use. If the specified database does not exist, it |
14 | + will be created. |
15 | + |
16 | optional arguments: |
17 | - server_ip - specify and ip and port for the server location. |
18 | - Defaults to http://127.0.0.1:5984 |
19 | - |
20 | + record_type - a string to specify the record_type to use in |
21 | + retrieving and creating records. Note that if no records are |
22 | + set the headings argument must also be used or a RuntimeError |
23 | + will result. |
24 | + |
25 | + headings - a list of strings specifying headings to use in |
26 | + the columns of the CouchWidget. |
27 | + |
28 | + Note that the CouchWidget uses headings for keys, and visa versa. |
29 | + If a record does not contain a value for a specified value |
30 | + the CouchWidget will simply display an empty string for the |
31 | + value. If the widget is set to editable, the user will be able |
32 | + to add values to the database. |
33 | + |
34 | """ |
35 | |
36 | gtk.TreeView.__init__(self) |
37 | |
38 | - #assume a local server |
39 | - self.server_ip = server_ip |
40 | - |
41 | #set up the default values |
42 | - self.__db = None |
43 | + if type(database_name) is not type(str()): |
44 | + raise TypeError("database_name is required and must be a string") |
45 | + |
46 | + #set up default values |
47 | self.__record_type = None |
48 | - self.__headings = None |
49 | + self.__headings = headings |
50 | self.__editable = False |
51 | + |
52 | + #set the datatabase |
53 | + self.database = database_name |
54 | + if record_type is not None: |
55 | + self.record_type = record_type |
56 | + |
57 | self.get_selection().set_mode(gtk.SELECTION_MULTIPLE) |
58 | |
59 | @property |
60 | @@ -84,7 +106,7 @@ |
61 | |
62 | #refresh the treeview if possible |
63 | if self.record_type != None: |
64 | - self.__populate_treeview()(self.record_type) |
65 | + self.__populate_treeview() |
66 | |
67 | @property |
68 | def database(self): |
69 | @@ -96,7 +118,7 @@ |
70 | |
71 | @database.setter |
72 | def database(self, db_name): |
73 | - self.__db = CouchDatabase(db_name, uri=self.server_ip, create=True) |
74 | + self.__db = CouchDatabase(db_name, create=True) |
75 | |
76 | if self.record_type != None: |
77 | self.__populate_treeview()(self.record_type) |
78 | @@ -119,30 +141,51 @@ |
79 | |
80 | def __populate_treeview(self): |
81 | #if the database is not set up, just return |
82 | - if self.__db == None or self.__headings == None: |
83 | + if self.__db == None: |
84 | return |
85 | |
86 | - #refresh the model structure |
87 | - self.__reset_model() |
88 | + #the cases with headings are complex and hacky |
89 | + #there are the following cases: |
90 | + #1. headings have not been set and cannot be inferred |
91 | + #2. headings have been previously set |
92 | + #3. headings should be inferred from results |
93 | + |
94 | |
95 | #retrieve the docs for the record_type, if any |
96 | results = self.__db.get_records(record_type=self.__record_type,create_view=True) |
97 | |
98 | + #test for headings case 1 and raise an exception |
99 | + if len(results) < 1 and self.headings == None: |
100 | + raise RuntimeError("""Cannot infer columns for CouchWidget. |
101 | + Ensure that records exist, or define columns using |
102 | + CouchWidget.headings property. |
103 | + """) |
104 | + |
105 | + #test for case 2, and set up the model |
106 | + if self.headings is not None: |
107 | + self.__reset_model() |
108 | + |
109 | #if headings are already assigned, set up headings and columns |
110 | + #for headings case 2 with or without results |
111 | if self.headings != None: |
112 | self.__make_headings() |
113 | |
114 | first_row = True |
115 | for r in results: |
116 | + #handle headings case 3 |
117 | #if headings are not set up, infer them from |
118 | - #the first stored record |
119 | if self.headings == None and first_row: |
120 | self.headings = [] |
121 | for k in r.value.keys(): |
122 | if not k.startswith("_") and k != "record_type": |
123 | self.headings.append(k) |
124 | #now set up the columns and headers |
125 | - self.__make_headings() |
126 | + self.__make_headings() |
127 | + |
128 | + #in headings case 3 the model has not been set yet |
129 | + #so do that now |
130 | + self.__reset_model() |
131 | + |
132 | first_row = False |
133 | |
134 | #lists have to match the list_store columns in length |
135 | @@ -187,6 +230,9 @@ |
136 | |
137 | """ |
138 | |
139 | + #TODO: add gaurd conditions |
140 | + #throw if no database is set |
141 | + |
142 | #lists have to match the list_store columns in length |
143 | #note that the last value is reserved for the doc_id |
144 | col_count = len(self.headings) + 1 |
145 | @@ -306,7 +352,7 @@ |
146 | This function is typically called when properties are set |
147 | |
148 | """ |
149 | - |
150 | + |
151 | #remove the current columns from the TreeView |
152 | cols = self.get_columns() |
153 | for c in cols: |
154 | @@ -319,7 +365,7 @@ |
155 | |
156 | def __make_headings(self): |
157 | """ __make_headings - internal funciton do not call direclty |
158 | - Sets up the headings and columnss for the TreeView. |
159 | + Sets up the headings and columns for the TreeView. |
160 | |
161 | """ |
162 | |
163 | @@ -408,19 +454,11 @@ |
164 | win.add(vbox) |
165 | |
166 | #create a test widget with test database values |
167 | - cw = CouchWidget() |
168 | - cw.database = "couch_widget_test" |
169 | + cw = CouchWidget("couch_widget_test", record_type = "test_record_type", headings = ["Key1","Key2","Key3","Key4"]) |
170 | |
171 | #allow editing |
172 | cw.editable = True |
173 | |
174 | - #create headers/keys |
175 | - cw.headings = ["Key1","Key2","Key3","Key4"] |
176 | - |
177 | - #set the record_type for the TreeView |
178 | - #it will not populate without this value being set |
179 | - cw.record_type = "test_record_type" |
180 | - |
181 | #create a row with all four columns set |
182 | cw.append_row(["val1","val2","val3","val4"]) |
183 | |
184 | @@ -442,12 +480,10 @@ |
185 | tv.show() |
186 | cw.connect("cursor-changed",__show_selected, (tv,cw)) |
187 | |
188 | - |
189 | #create ui for testing selection |
190 | id_vbox = gtk.VBox(False, 5) |
191 | id_vbox.show() |
192 | |
193 | - |
194 | fb_lbl = gtk.Label("paste ids into the edit box to select them") |
195 | fb_lbl.show() |
196 | |
197 | |
198 | === modified file 'desktopcouch/records/tests/test_couchwidget.py' |
199 | --- desktopcouch/records/tests/test_couchwidget.py 2009-08-19 23:01:09 +0000 |
200 | +++ desktopcouch/records/tests/test_couchwidget.py 2009-08-20 21:32:18 +0000 |
201 | @@ -19,6 +19,7 @@ |
202 | """Tests for the couchwidget object""" |
203 | |
204 | from testtools import TestCase |
205 | +from desktopcouch.records.record import Record |
206 | from desktopcouch.records.server import CouchDatabase |
207 | from desktopcouch.records.couchwidget import CouchWidget |
208 | |
209 | @@ -28,59 +29,170 @@ |
210 | self.dbname = "couch_widget_test" |
211 | self.record_type = "test_record_type" |
212 | |
213 | - def test_headings_first(self): |
214 | - #create a test widget with test database values |
215 | - cw = CouchWidget() |
216 | - cw.database = self.dbname |
217 | - |
218 | - #allow editing |
219 | - cw.editable = True |
220 | - |
221 | - #create headers/keys |
222 | - cw.headings = ["Key1","Key2","Key3","Key4"] |
223 | - |
224 | - #set the record_type for the TreeView |
225 | - #it will not populate without this value being set |
226 | - cw.record_type = self.record_type |
227 | - |
228 | - #create a row with all four columns set |
229 | - cw.append_row(["val1","val2","val3","val4"]) |
230 | - |
231 | - #create a row with only the second column set |
232 | - cw.append_row(["","val2"]) |
233 | - |
234 | - #create an empty row (which will not be saved until the user edits it) |
235 | - cw.append_row([]) |
236 | - |
237 | - self.assertEqual(cw.record_type, self.record_type) |
238 | - self.__delete_db() |
239 | - |
240 | - def test_record_type_first(self): |
241 | - #create a test widget with test database values |
242 | - cw = CouchWidget() |
243 | - cw.database = self.dbname |
244 | - |
245 | - #allow editing |
246 | - cw.editable = True |
247 | - |
248 | - #set the record_type for the TreeView |
249 | - #it will not populate without this value being set |
250 | - cw.record_type = self.record_type |
251 | - |
252 | - #create headers/keys |
253 | - cw.headings = ["Key1","Key2","Key3","Key4"] |
254 | - |
255 | - #create a row with all four columns set |
256 | - cw.append_row(["val1","val2","val3","val4"]) |
257 | - |
258 | - #create a row with only the second column set |
259 | - cw.append_row(["","val2"]) |
260 | - |
261 | - #create an empty row (which will not be saved until the user edits it) |
262 | - cw.append_row([]) |
263 | - |
264 | - self.assertEqual(cw.record_type, self.record_type) |
265 | - self.__delete_db() |
266 | + def test_constructor_gaurded(self): |
267 | + """ensure that couchwidget cannot be constructed without a database name""" |
268 | + try: |
269 | + cw = CouchWidget(None) |
270 | + except TypeError, inst: |
271 | + self.assertEqual(inst.args[0],"database_name is required and must be a string") |
272 | + |
273 | + def test_new_rows_with_headings(self): |
274 | + """Test a simple creating a couchwidget """ |
275 | + |
276 | + #create a test widget with test database values |
277 | + cw = CouchWidget(self.dbname) |
278 | + |
279 | + #allow editing |
280 | + cw.editable = True |
281 | + |
282 | + #create headers/keys |
283 | + cw.headings = ["Key1","Key2","Key3","Key4"] |
284 | + |
285 | + #set the record_type for the TreeView |
286 | + #it will not populate without this value being set |
287 | + cw.record_type = self.record_type |
288 | + |
289 | + #create a row with all four columns set |
290 | + cw.append_row(["val1","val2","val3","val4"]) |
291 | + |
292 | + #create a row with only the second column set |
293 | + cw.append_row(["","val2"]) |
294 | + |
295 | + #create an empty row (which will not be saved until the user edits it) |
296 | + cw.append_row([]) |
297 | + |
298 | + #if this all worked, there should be three rows in the model |
299 | + model = cw.get_model() |
300 | + self.assertEqual(len(model), 3) |
301 | + |
302 | + #clean up for the next test |
303 | + self.__delete_db() |
304 | + |
305 | + def test_no_headings_or_stored_records(self): |
306 | + """test when there is no defined headings and no stored records |
307 | + to infer headings from. Should raise a proper exception. |
308 | + """ |
309 | + |
310 | + try: |
311 | + #create a test widget with test database values |
312 | + cw = CouchWidget(self.dbname) |
313 | + |
314 | + #set the record_type for the TreeView |
315 | + #it will not populate without this value being set |
316 | + cw.record_type = self.record_type |
317 | + |
318 | + #create a row with all four columns set |
319 | + cw.append_row(["val1","val2","val3","val4"]) |
320 | + |
321 | + #create a row with only the second column set |
322 | + cw.append_row(["","val2"]) |
323 | + |
324 | + #create an empty row (which will not be saved until the user edits it) |
325 | + cw.append_row([]) |
326 | + |
327 | + #if this all worked, there should be three rows in the model |
328 | + model = cw.get_model() |
329 | + |
330 | + #should be catching the following exception |
331 | + except RuntimeError, inst: |
332 | + self.assertEquals(inst.args[0].find("Cannot infer columns for CouchWidget"),0) |
333 | + |
334 | + #clean up for the next test |
335 | + self.__delete_db() |
336 | + |
337 | + def test_all_from_database(self): |
338 | + #create some records |
339 | + db = CouchDatabase(self.dbname, create=True) |
340 | + db.put_record(Record({"key1_1":"val1_1","key1_2":"val1_2","key1_3":"val1_3","record_type":self.record_type})) |
341 | + db.put_record(Record({"key2_1":"val2_1","key2_2":"val2_2","key2_3":"val2_3","record_type":self.record_type})) |
342 | + |
343 | + #build the couchwidget |
344 | + cw = CouchWidget(self.dbname) |
345 | + cw.record_type = self.record_type |
346 | + #make sure there are three columns and two rows |
347 | + self.assertEqual(cw.get_model().get_n_columns(),4) |
348 | + self.assertEqual(len(cw.get_model()),2) |
349 | + |
350 | + #clean up for the next test |
351 | + self.__delete_db() |
352 | + |
353 | + def test_single_col_from_database(self): |
354 | + #create some records |
355 | + db = CouchDatabase(self.dbname, create=True) |
356 | + db.put_record(Record({"key1_1":"val1_1","key1_2":"val1_2","key1_3":"val1_3","record_type":self.record_type})) |
357 | + db.put_record(Record({"key1_1":"val2_1","key1_2":"val2_2","key1_3":"val2_3","record_type":self.record_type})) |
358 | + |
359 | + #build the couchwidget |
360 | + cw = CouchWidget(self.dbname) |
361 | + cw.headings = ["key1_1"] |
362 | + cw.record_type = self.record_type |
363 | + #make sure there are three columns and two rows |
364 | + self.assertEqual(cw.get_model().get_n_columns(),2) |
365 | + self.assertEqual(len(cw.get_model()),2) |
366 | + |
367 | + #clean up for the next test |
368 | + self.__delete_db() |
369 | + |
370 | + def test_optional_record_type_arg(self): |
371 | + """Test a simple creating a couchwidget """ |
372 | + #create some records |
373 | + db = CouchDatabase(self.dbname, create=True) |
374 | + db.put_record(Record({"key1_1":"val1_1","key1_2":"val1_2","key1_3":"val1_3","record_type":self.record_type})) |
375 | + db.put_record(Record({"key2_1":"val2_1","key2_2":"val2_2","key2_3":"val2_3","record_type":self.record_type})) |
376 | + |
377 | + #create a test widget with test database values |
378 | + cw = CouchWidget(self.dbname, record_type = self.record_type) |
379 | + |
380 | + #make sure there are three columns and two rows |
381 | + self.assertEqual(cw.get_model().get_n_columns(),4) |
382 | + self.assertEqual(len(cw.get_model()),2) |
383 | + |
384 | + #clean up for the next test |
385 | + self.__delete_db() |
386 | + |
387 | + def test_optional_args_no_stored_records(self): |
388 | + """Test a simple creating a couchwidget """ |
389 | + |
390 | + #create a test widget with test database values |
391 | + cw = CouchWidget(self.dbname, record_type = self.record_type, headings = ["Key1","Key2","Key3","Key4"]) |
392 | + |
393 | + |
394 | + #create a row with all four columns set |
395 | + cw.append_row(["val1","val2","val3","val4"]) |
396 | + |
397 | + #create a row with only the second column set |
398 | + cw.append_row(["","val2"]) |
399 | + |
400 | + #create an empty row (which will not be saved until the user edits it) |
401 | + cw.append_row([]) |
402 | + |
403 | + #if this all worked, there should be three rows in the model |
404 | + model = cw.get_model() |
405 | + self.assertEqual(len(model), 3) |
406 | + |
407 | + #clean up for the next test |
408 | + self.__delete_db() |
409 | + |
410 | + def test_programatically_add_row(self): |
411 | + """test appending different sized rows programatically""" |
412 | + #create some records |
413 | + db = CouchDatabase(self.dbname, create=True) |
414 | + db.put_record(Record({"key1_1":"val1_1","key1_2":"val1_2","key1_3":"val1_3","record_type":self.record_type})) |
415 | + db.put_record(Record({"key2_1":"val2_1","key2_2":"val2_2","key2_3":"val2_3","record_type":self.record_type})) |
416 | + |
417 | + #create a test widget with test database values |
418 | + cw = CouchWidget(self.dbname, record_type = self.record_type) |
419 | + |
420 | + #allow editing |
421 | + cw.append_row(["boo","ray"]) |
422 | + |
423 | + #make sure there are three columns and two rows |
424 | + self.assertEqual(cw.get_model().get_n_columns(),4) |
425 | + self.assertEqual(len(cw.get_model()),3) |
426 | + |
427 | + #clean up for the next test |
428 | + self.__delete_db() |
429 | + |
430 | |
431 | def tearDown(self): |
432 | """tear down each test""" |
433 | @@ -91,4 +203,5 @@ |
434 | db = CouchDatabase(self.dbname, create=True) |
435 | del db._server[self.dbname] |
436 | |
437 | + |
438 |
Note that this branch breaks the CouchWidget API by requiring a database be specified when the widget is created.
This branch fixes bug 412266.
This branch adds more tests for CouchWidget, and improves the tests that existed.