Merge lp://qastaging/~rick-rickspencer3/desktopcouch/CouchWidget-tests-and-robustness into lp://qastaging/desktopcouch

Proposed by Rick Spencer
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
Reviewer Review Type Date Requested Status
Rodrigo Moya (community) Approve
Eric Casteleijn (community) Approve
Review via email: mp+10488@code.qastaging.launchpad.net

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.

To post a comment you must log in.
Revision history for this message
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.

Revision history for this message
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

Revision history for this message
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.

review: Approve
Revision history for this message
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

review: Needs Fixing
Revision history for this message
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

Revision history for this message
Rodrigo Moya (rodrigo-moya) :
review: Approve

Preview Diff

[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
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

Subscribers

People subscribed via source and target branches