]> git.madduck.net Git - etc/taskwarrior.git/blob - tasklib/tests.py

madduck's git repository

Every one of the projects in this repository is available at the canonical URL git://git.madduck.net/madduck/pub/<projectpath> — see each project's metadata for the exact URL.

All patches and comments are welcome. Please squash your changes to logical commits before using git-format-patch and git-send-email to patches@git.madduck.net. If you'd read over the Git project's submission guidelines and adhered to them, I'd be especially grateful.

SSH access, as well as push access can be individually arranged.

If you use my repositories frequently, consider adding the following snippet to ~/.gitconfig and using the third clone URL listed for each project:

[url "git://git.madduck.net/madduck/"]
  insteadOf = madduck:

Tests: Cover setting read only attributes
[etc/taskwarrior.git] / tasklib / tests.py
1 # coding=utf-8
2
3 import datetime
4 import shutil
5 import tempfile
6 import unittest
7
8 from .task import TaskWarrior, Task
9
10
11 class TasklibTest(unittest.TestCase):
12
13     def setUp(self):
14         self.tmp = tempfile.mkdtemp(dir='.')
15         self.tw = TaskWarrior(data_location=self.tmp)
16
17     def tearDown(self):
18         shutil.rmtree(self.tmp)
19
20
21 class TaskFilterTest(TasklibTest):
22
23     def test_all_empty(self):
24         self.assertEqual(len(self.tw.tasks.all()), 0)
25
26     def test_all_non_empty(self):
27         Task(self.tw, description="test task").save()
28         self.assertEqual(len(self.tw.tasks.all()), 1)
29         self.assertEqual(self.tw.tasks.all()[0]['description'], 'test task')
30         self.assertEqual(self.tw.tasks.all()[0]['status'], 'pending')
31
32     def test_pending_non_empty(self):
33         Task(self.tw, description="test task").save()
34         self.assertEqual(len(self.tw.tasks.pending()), 1)
35         self.assertEqual(self.tw.tasks.pending()[0]['description'],
36                          'test task')
37         self.assertEqual(self.tw.tasks.pending()[0]['status'], 'pending')
38
39     def test_completed_empty(self):
40         Task(self.tw, description="test task").save()
41         self.assertEqual(len(self.tw.tasks.completed()), 0)
42
43     def test_completed_non_empty(self):
44         Task(self.tw, description="test task").save()
45         self.assertEqual(len(self.tw.tasks.completed()), 0)
46         self.tw.tasks.all()[0].done()
47         self.assertEqual(len(self.tw.tasks.completed()), 1)
48
49     def test_filtering_by_attribute(self):
50         Task(self.tw, description="no priority task").save()
51         Task(self.tw, priority="H", description="high priority task").save()
52         self.assertEqual(len(self.tw.tasks.all()), 2)
53
54         # Assert that the correct number of tasks is returned
55         self.assertEqual(len(self.tw.tasks.filter(priority="H")), 1)
56
57         # Assert that the correct tasks are returned
58         high_priority_task = self.tw.tasks.get(priority="H")
59         self.assertEqual(high_priority_task['description'], "high priority task")
60
61     def test_filtering_by_empty_attribute(self):
62         Task(self.tw, description="no priority task").save()
63         Task(self.tw, priority="H", description="high priority task").save()
64         self.assertEqual(len(self.tw.tasks.all()), 2)
65
66         # Assert that the correct number of tasks is returned
67         self.assertEqual(len(self.tw.tasks.filter(priority=None)), 1)
68
69         # Assert that the correct tasks are returned
70         no_priority_task = self.tw.tasks.get(priority=None)
71         self.assertEqual(no_priority_task['description'], "no priority task")
72
73
74 class TaskTest(TasklibTest):
75
76     def test_create_unsaved_task(self):
77         # Make sure a new task is not saved unless explicitly called for
78         t = Task(self.tw, description="test task")
79         self.assertEqual(len(self.tw.tasks.all()), 0)
80
81     # TODO: once python 2.6 compatiblity is over, use context managers here
82     #       and in all subsequent tests for assertRaises
83
84     def test_delete_unsaved_task(self):
85         t = Task(self.tw, description="test task")
86         self.assertRaises(Task.NotSaved, t.delete)
87
88     def test_complete_unsaved_task(self):
89         t = Task(self.tw, description="test task")
90         self.assertRaises(Task.NotSaved, t.done)
91
92     def test_refresh_unsaved_task(self):
93         t = Task(self.tw, description="test task")
94         self.assertRaises(Task.NotSaved, t.refresh)
95
96     def test_delete_deleted_task(self):
97         t = Task(self.tw, description="test task")
98         t.save()
99         t.delete()
100
101         self.assertRaises(Task.DeletedTask, t.delete)
102
103     def test_complete_completed_task(self):
104         t = Task(self.tw, description="test task")
105         t.save()
106         t.done()
107
108         self.assertRaises(Task.CompletedTask, t.done)
109
110     def test_complete_deleted_task(self):
111         t = Task(self.tw, description="test task")
112         t.save()
113         t.delete()
114
115         self.assertRaises(Task.DeletedTask, t.done)
116
117     def test_modify_simple_attribute_without_space(self):
118         t = Task(self.tw, description="test")
119         t.save()
120
121         self.assertEquals(t['description'], "test")
122
123         t['description'] = "test-modified"
124         t.save()
125
126         self.assertEquals(t['description'], "test-modified")
127
128     def test_modify_simple_attribute_with_space(self):
129         # Space can pose problems with parsing
130         t = Task(self.tw, description="test task")
131         t.save()
132
133         self.assertEquals(t['description'], "test task")
134
135         t['description'] = "test task modified"
136         t.save()
137
138         self.assertEquals(t['description'], "test task modified")
139
140     def test_empty_dependency_set_of_unsaved_task(self):
141         t = Task(self.tw, description="test task")
142         self.assertEqual(t['depends'], set())
143
144     def test_empty_dependency_set_of_saved_task(self):
145         t = Task(self.tw, description="test task")
146         t.save()
147         self.assertEqual(t['depends'], set())
148
149     def test_set_unsaved_task_as_dependency(self):
150         # Adds only one dependency to task with no dependencies
151         t = Task(self.tw, description="test task")
152         dependency = Task(self.tw, description="needs to be done first")
153
154         # We only save the parent task, dependency task is unsaved
155         t.save()
156
157         self.assertRaises(Task.NotSaved,
158                           t.__setitem__, 'depends', set([dependency]))
159
160     def test_set_simple_dependency_set(self):
161         # Adds only one dependency to task with no dependencies
162         t = Task(self.tw, description="test task")
163         dependency = Task(self.tw, description="needs to be done first")
164
165         t.save()
166         dependency.save()
167
168         t['depends'] = set([dependency])
169
170         self.assertEqual(t['depends'], set([dependency]))
171
172     def test_set_complex_dependency_set(self):
173         # Adds two dependencies to task with no dependencies
174         t = Task(self.tw, description="test task")
175         dependency1 = Task(self.tw, description="needs to be done first")
176         dependency2 = Task(self.tw, description="needs to be done second")
177
178         t.save()
179         dependency1.save()
180         dependency2.save()
181
182         t['depends'] = set([dependency1, dependency2])
183
184         self.assertEqual(t['depends'], set([dependency1, dependency2]))
185
186     def test_remove_from_dependency_set(self):
187         # Removes dependency from task with two dependencies
188         t = Task(self.tw, description="test task")
189         dependency1 = Task(self.tw, description="needs to be done first")
190         dependency2 = Task(self.tw, description="needs to be done second")
191
192         dependency1.save()
193         dependency2.save()
194
195         t['depends'] = set([dependency1, dependency2])
196         t.save()
197
198         t['depends'] = t['depends'] - set([dependency2])
199         t.save()
200
201         self.assertEqual(t['depends'], set([dependency1]))
202
203     def test_add_to_dependency_set(self):
204         # Adds dependency to task with one dependencies
205         t = Task(self.tw, description="test task")
206         dependency1 = Task(self.tw, description="needs to be done first")
207         dependency2 = Task(self.tw, description="needs to be done second")
208
209         dependency1.save()
210         dependency2.save()
211
212         t['depends'] = set([dependency1])
213         t.save()
214
215         t['depends'] = t['depends'] | set([dependency2])
216         t.save()
217
218         self.assertEqual(t['depends'], set([dependency1, dependency2]))
219
220     def test_simple_dependency_set_save_repeatedly(self):
221         # Adds only one dependency to task with no dependencies
222         t = Task(self.tw, description="test task")
223         dependency = Task(self.tw, description="needs to be done first")
224         dependency.save()
225
226         t['depends'] = set([dependency])
227         t.save()
228
229         # We taint the task, but keep depends intact
230         t['description'] = "test task modified"
231         t.save()
232
233         self.assertEqual(t['depends'], set([dependency]))
234
235         # We taint the task, but assign the same set to the depends
236         t['depends'] = set([dependency])
237         t['description'] = "test task modified again"
238         t.save()
239
240         self.assertEqual(t['depends'], set([dependency]))
241
242     def test_compare_different_tasks(self):
243         # Negative: compare two different tasks
244         t1 = Task(self.tw, description="test task")
245         t2 = Task(self.tw, description="test task")
246
247         t1.save()
248         t2.save()
249
250         self.assertEqual(t1 == t2, False)
251
252     def test_compare_same_task_object(self):
253         # Compare Task object wit itself
254         t = Task(self.tw, description="test task")
255         t.save()
256
257         self.assertEqual(t == t, True)
258
259     def test_compare_same_task(self):
260         # Compare the same task using two different objects
261         t1 = Task(self.tw, description="test task")
262         t1.save()
263
264         t2 = self.tw.tasks.get(uuid=t1['uuid'])
265         self.assertEqual(t1 == t2, True)
266
267     def test_compare_unsaved_tasks(self):
268         # t1 and t2 are unsaved tasks, considered to be unequal
269         # despite the content of data
270         t1 = Task(self.tw, description="test task")
271         t2 = Task(self.tw, description="test task")
272
273         self.assertEqual(t1 == t2, False)
274
275     def test_hash_unsaved_tasks(self):
276         # Considered equal, it's the same object
277         t1 = Task(self.tw, description="test task")
278         t2 = t1
279         self.assertEqual(hash(t1) == hash(t2), True)
280
281     def test_hash_same_task(self):
282         # Compare the hash of the task using two different objects
283         t1 = Task(self.tw, description="test task")
284         t1.save()
285
286         t2 = self.tw.tasks.get(uuid=t1['uuid'])
287         self.assertEqual(t1.__hash__(), t2.__hash__())
288
289     def test_adding_task_with_priority(self):
290         t = Task(self.tw, description="test task", priority="M")
291         t.save()
292
293     def test_removing_priority_with_none(self):
294         t = Task(self.tw, description="test task", priority="L")
295         t.save()
296
297         # Remove the priority mark
298         t['priority'] = None
299         t.save()
300
301         # Assert that priority is not there after saving
302         self.assertEqual(t['priority'], None)
303
304     def test_adding_task_with_due_time(self):
305         t = Task(self.tw, description="test task", due=datetime.datetime.now())
306         t.save()
307
308     def test_removing_due_time_with_none(self):
309         t = Task(self.tw, description="test task", due=datetime.datetime.now())
310         t.save()
311
312         # Remove the due timestamp
313         t['due'] = None
314         t.save()
315
316         # Assert that due timestamp is no longer there
317         self.assertEqual(t['due'], None)
318
319     def test_modified_fields_new_task(self):
320         t = Task(self.tw)
321
322         # This should be empty with new task
323         self.assertEqual(set(t._modified_fields), set())
324
325         # Modify the task
326         t['description'] = "test task"
327         self.assertEqual(set(t._modified_fields), set(['description']))
328
329         t['due'] = datetime.datetime(2014, 2, 14, 14, 14, 14)  # <3
330         self.assertEqual(set(t._modified_fields), set(['description', 'due']))
331
332         t['project'] = "test project"
333         self.assertEqual(set(t._modified_fields),
334                          set(['description', 'due', 'project']))
335
336         # List of modified fields should clear out when saved
337         t.save()
338         self.assertEqual(set(t._modified_fields), set())
339
340         # Reassigning the fields with the same values now should not produce
341         # modified fields
342         t['description'] = "test task"
343         t['due'] = datetime.datetime(2014, 2, 14, 14, 14, 14)  # <3
344         t['project'] = "test project"
345         self.assertEqual(set(t._modified_fields), set())
346
347     def test_modified_fields_loaded_task(self):
348         t = Task(self.tw)
349
350         # Modify the task
351         t['description'] = "test task"
352         t['due'] = datetime.datetime(2014, 2, 14, 14, 14, 14)  # <3
353         t['project'] = "test project"
354
355         dependency = Task(self.tw, description="dependency")
356         dependency.save()
357         t['depends'] = set([dependency])
358
359         # List of modified fields should clear out when saved
360         t.save()
361         self.assertEqual(set(t._modified_fields), set())
362
363         # Get the task by using a filter by UUID
364         t2 = self.tw.tasks.get(uuid=t['uuid'])
365
366         # Reassigning the fields with the same values now should not produce
367         # modified fields
368         t['description'] = "test task"
369         t['due'] = datetime.datetime(2014, 2, 14, 14, 14, 14)  # <3
370         t['project'] = "test project"
371         t['depends'] = set([dependency])
372         self.assertEqual(set(t._modified_fields), set())
373
374     def test_setting_read_only_attrs_through_init(self):
375         # Test that we are unable to set readonly attrs through __init__
376         for readonly_key in Task.read_only_fields:
377             kwargs = {'description': 'test task', readonly_key: 'value'}
378             self.assertRaises(RuntimeError,
379                               lambda: Task(self.tw, **kwargs))
380
381     def test_setting_read_only_attrs_through_setitem(self):
382         # Test that we are unable to set readonly attrs through __init__
383         for readonly_key in Task.read_only_fields:
384             t = Task(self.tw, description='test task')
385             self.assertRaises(RuntimeError,
386                               lambda: t.__setitem__(readonly_key, 'value'))
387
388
389 class AnnotationTest(TasklibTest):
390
391     def setUp(self):
392         super(AnnotationTest, self).setUp()
393         Task(self.tw, description="test task").save()
394
395     def test_adding_annotation(self):
396         task = self.tw.tasks.get()
397         task.add_annotation('test annotation')
398         self.assertEqual(len(task['annotations']), 1)
399         ann = task['annotations'][0]
400         self.assertEqual(ann['description'], 'test annotation')
401
402     def test_removing_annotation(self):
403         task = self.tw.tasks.get()
404         task.add_annotation('test annotation')
405         ann = task['annotations'][0]
406         ann.remove()
407         self.assertEqual(len(task['annotations']), 0)
408
409     def test_removing_annotation_by_description(self):
410         task = self.tw.tasks.get()
411         task.add_annotation('test annotation')
412         task.remove_annotation('test annotation')
413         self.assertEqual(len(task['annotations']), 0)
414
415     def test_removing_annotation_by_obj(self):
416         task = self.tw.tasks.get()
417         task.add_annotation('test annotation')
418         ann = task['annotations'][0]
419         task.remove_annotation(ann)
420         self.assertEqual(len(task['annotations']), 0)
421
422
423 class UnicodeTest(TasklibTest):
424
425     def test_unicode_task(self):
426         Task(self.tw, description="†åßk").save()
427         self.tw.tasks.get()
428
429     def test_non_unicode_task(self):
430         Task(self.tw, description="test task").save()
431         self.tw.tasks.get()