GTG

Merge lp:~jml/gtg/more-tests into lp:~gtg/gtg/old-trunk

Proposed by Jonathan Lange
Status: Merged
Merged at revision: not available
Proposed branch: lp:~jml/gtg/more-tests
Merge into: lp:~gtg/gtg/old-trunk
Diff against target: None lines
To merge this branch: bzr merge lp:~jml/gtg/more-tests
Reviewer Review Type Date Requested Status
Gtg developers Pending
Review via email: mp+8933@code.launchpad.net
To post a comment you must log in.
Revision history for this message
Jonathan Lange (jml) wrote :

The main purpose of this branch is to add tests for the Tag class.

The tests that I've added were designed to verify the current behaviour of the class. All of the tests pass running against the version of Tag that's in trunk. Two of the tests check behaviour that looks to me to be accidental, but I figured I'd add them and let someone who knows more than me make an informed decision.

Anyway, once the tests were written I took the opportunity to:
  a) Add docstrings for Tag
  b) Clean up whitespace
  c) Fix PEP 8 violations (remove spaces before colons, add spaces after commas, drop haskey usage)
  d) Changed the internal attributes to have underscore prefixes.
  e) Removed the "defensive" thing in get_all_attributes, since if ever 'name' isn't an attribute, it's actually a serious error.
  f) Changed set_attribute so that 'name' is slightly less special cased.

I think all of these changes are good ones, but tastes may vary.

jml

Preview Diff

[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
=== modified file 'GTG/core/tagstore.py'
--- GTG/core/tagstore.py 2009-03-31 09:29:10 +0000
+++ GTG/core/tagstore.py 2009-07-17 11:39:38 +0000
@@ -149,45 +149,71 @@
149#########################################################################149#########################################################################
150######################### Tag ###########################################150######################### Tag ###########################################
151151
152#A tag is defined by its name (in most cases, it will be "@something") and it can have multiple attributes152class Tag:
153class Tag :153 """A short name that can be applied to Tasks.
154154
155 def __init__(self,name,save_cllbk=None) :155 I mean, surely you must know what a tag is by now. Think Gmail,
156 self.attributes = {}156 del.icio.us, Flickr et al.
157 self.name = name157
158 self.set_attribute("name",self.name)158 A tag is defined by its name, which in most cases is '@something'. A tag
159 self.save = save_cllbk159 can also have multiple arbitrary attributes. The only attribute enforced
160 160 for tags is 'name', which always matches `Tag.get_name()`.
161 def get_name(self) :161 """
162
163 def __init__(self, name, save_cllbk=None):
164 """Construct a tag.
165
166 :param name: The name of the tag. Should be a string, generally a
167 short one.
168 :param save_cllbk: A nullary callable, called whenever an attribute
169 is set.
170 """
171 self._name = str(name)
172 self._attributes = {'name': self._name}
173 self._save = save_cllbk
174
175 def get_name(self):
176 """Return the name of the tag."""
162 return self.get_attribute("name")177 return self.get_attribute("name")
163 178
164 def set_attribute(self,att_name,att_value) :179 def set_attribute(self, att_name, att_value):
165 #warning : only the constructor can set the "name" 180 """Set an arbitrary attribute.
166 if att_name != "name" :181
167 #Attributes should all be strings182 This will call the 'save_cllbk' callback passed to the constructor.
168 val = unicode(str(att_value),"UTF-8")183
169 self.attributes[att_name] = val184 :param att_name: The name of the attribute.
170 self.save()185 :param att_value: The value of the attribute. Will be converted to a
171 elif self.name == att_value :186 string.
172 self.attributes[att_name] = str(att_value)187 """
173 188 if att_name == "name":
174 def get_attribute(self,att_name) :189 # Warning : only the constructor can set the "name".
175 if self.attributes.has_key(att_name) :190 #
176 return self.attributes[att_name]191 # XXX: This should actually raise an exception, or warn, or
177 else :192 # something. The Zen of Python says "Errors should never pass
178 return None193 # silently." -- jml, 2009-07-17
179 194 return
180 #if butname argument is set, the "name" attributes is removed195 # Attributes should all be strings.
181 #from the list196 val = unicode(str(att_value), "UTF-8")
182 def get_all_attributes(self,butname=False) :197 self._attributes[att_name] = val
183 l = self.attributes.keys()198 self._save()
184 if butname :199
185 #Normally this condition is not necessary200 def get_attribute(self, att_name):
186 #Defensiveness...201 """Get the attribute 'att_name'.
187 if "name" in l :202
188 l.remove("name")203 Returns None if there is no attribute matching 'att_name'.
189 return l204 """
190 205 return self._attributes.get(att_name, None)
206
207 def get_all_attributes(self, butname=False):
208 """Return a list of all attribute names.
209
210 :param butname: If True, exclude 'name' from the list of attribute
211 names.
212 """
213 attributes = self._attributes.keys()
214 if butname:
215 attributes.remove('name')
216 return attributes
217
191 def __str__(self):218 def __str__(self):
192 return "Tag: %s" %self.get_name()219 return "Tag: %s" % self.get_name()
193
194220
=== modified file 'GTG/tests/__init__.py'
--- GTG/tests/__init__.py 2009-07-14 12:09:55 +0000
+++ GTG/tests/__init__.py 2009-07-17 08:35:00 +0000
@@ -21,10 +21,14 @@
2121
22import unittest22import unittest
2323
24from GTG.tests import test_backends24from GTG.tests import (
25 test_backends,
26 test_tagstore,
27 )
2528
2629
27def test_suite():30def test_suite():
28 return unittest.TestSuite([31 return unittest.TestSuite([
29 test_backends.test_suite(),32 test_backends.test_suite(),
33 test_tagstore.test_suite(),
30 ])34 ])
3135
=== added file 'GTG/tests/test_tagstore.py'
--- GTG/tests/test_tagstore.py 1970-01-01 00:00:00 +0000
+++ GTG/tests/test_tagstore.py 2009-07-17 10:24:08 +0000
@@ -0,0 +1,122 @@
1# -*- coding: utf-8 -*-
2# -----------------------------------------------------------------------------
3# Gettings Things Gnome! - a personnal organizer for the GNOME desktop
4# Copyright (c) 2008-2009 - Lionel Dricot & Bertrand Rousseau
5#
6# This program is free software: you can redistribute it and/or modify it under
7# the terms of the GNU General Public License as published by the Free Software
8# Foundation, either version 3 of the License, or (at your option) any later
9# version.
10#
11# This program is distributed in the hope that it will be useful, but WITHOUT
12# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
13# FOR A PARTICULAR PURPOSE. See the GNU General Public License for more
14# details.
15#
16# You should have received a copy of the GNU General Public License along with
17# this program. If not, see <http://www.gnu.org/licenses/>.
18# -----------------------------------------------------------------------------
19
20"""Tests for the tagstore."""
21
22import unittest
23
24from GTG.core.tagstore import Tag
25
26
27class TestTag(unittest.TestCase):
28 """Tests for `Tag`."""
29
30 def test_name(self):
31 # The first argument to the Tag constructor is the name, which you can
32 # get with get_name().
33 tag = Tag('foo')
34 self.assertEqual('foo', tag.get_name())
35
36 def test_name_is_attribute(self):
37 # The name of the tag is also stored as an attribute.
38 tag = Tag('foo')
39 self.assertEqual('foo', tag.get_attribute('name'))
40
41 def test_missing_attribute_returns_none(self):
42 # If get_attribute is called for an attribute that doesn't exist, it
43 # returns None.
44 tag = Tag('whatever')
45 result = tag.get_attribute('no-such-attribute')
46 self.assertEqual(None, result)
47
48 def test_set_attribute_calls_save(self):
49 # Calling set_attribute calls the 'save_cllbk'.
50 save_calls = []
51 tag = Tag('whatever', lambda: save_calls.append(None))
52 tag.set_attribute('new-attribute', 'value')
53 self.assertEqual(1, len(save_calls))
54
55 def test_set_then_get_attribute(self):
56 # Attributes set with set_attribute can be retrieved with
57 # get_attribute.
58 tag = Tag('whatever', lambda: None)
59 tag.set_attribute('new-attribute', 'value')
60 result = tag.get_attribute('new-attribute')
61 self.assertEqual('value', result)
62
63 def test_set_non_str_attribute_casts_to_string(self):
64 # If the value of the attribute passed to set_attribute is not a
65 # string, it's cast to a string.
66 tag = Tag('whatever', lambda: None)
67 tag.set_attribute('new-attribute', 42)
68 result = tag.get_attribute('new-attribute')
69 self.assertEqual('42', result)
70
71 def test_get_all_attributes_initial(self):
72 # Initially, a Tag has only the name attribute.
73 tag = Tag('foo')
74 self.assertEqual(['name'], tag.get_all_attributes())
75
76 def test_get_all_attributes_after_setting(self):
77 # After attributes are set, get_all_attributes includes those
78 # attributes. The order is not guaranteed.
79 tag = Tag('foo', lambda: None)
80 tag.set_attribute('bar', 'baz')
81 self.assertEqual(set(['name', 'bar']), set(tag.get_all_attributes()))
82
83 def test_get_all_but_name(self):
84 # If 'butname' is True, then exclude the 'name' attribute.
85 tag = Tag('foo', lambda: None)
86 self.assertEqual([], tag.get_all_attributes(butname=True))
87 tag.set_attribute('bar', 'baz')
88 self.assertEqual(['bar'], tag.get_all_attributes(butname=True))
89
90 def test_str(self):
91 # str(tag) is 'Tag: <name>'
92 tag = Tag('foo')
93 self.assertEqual('Tag: foo', str(tag))
94
95 def test_set_name_attribute_does_nothing(self):
96 # The 'name' attribute is set by the constructor. After it is set, it
97 # cannot be changed with further calls to set_attribute.
98 tag = Tag('old', lambda: None)
99 tag.set_attribute('name', 'new')
100 self.assertEqual('old', tag.get_name())
101 self.assertEqual('old', tag.get_attribute('name'))
102
103 # XXX: The following tests check the current behaviour of the Tag class,
104 # but I'm not sure if they're correct behaviour. -- jml, 2009-07-17
105
106 def test_save_not_called_on_construction(self):
107 # The save callback isn't called by the constructor, despite the fact
108 # that it sets the name attribute.
109 save_calls = []
110 Tag('old', lambda: save_calls.append(None))
111 self.assertEqual(0, len(save_calls))
112
113 def test_set_name_doesnt_call_save(self):
114 # Setting the name attribute doesn't call save.
115 save_calls = []
116 tag = Tag('old', lambda: save_calls.append(None))
117 tag.set_attribute('name', 'new')
118 self.assertEqual(0, len(save_calls))
119
120
121def test_suite():
122 return unittest.TestLoader().loadTestsFromName(__name__)

Subscribers

People subscribed via source and target branches

to status/vote changes: