]> 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:

Merge branch 'release/0.7.0' into develop
[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     def test_filter_for_task_with_space_in_descripition(self):
74         task = Task(self.tw, description="test task")
75         task.save()
76
77         filtered_task = self.tw.tasks.get(description="test task")
78         self.assertEqual(filtered_task['description'], "test task")
79
80     def test_filter_for_task_without_space_in_descripition(self):
81         task = Task(self.tw, description="test")
82         task.save()
83
84         filtered_task = self.tw.tasks.get(description="test")
85         self.assertEqual(filtered_task['description'], "test")
86
87     def test_filter_for_task_with_space_in_project(self):
88         task = Task(self.tw, description="test", project="random project")
89         task.save()
90
91         filtered_task = self.tw.tasks.get(project="random project")
92         self.assertEqual(filtered_task['project'], "random project")
93
94     def test_filter_for_task_without_space_in_project(self):
95         task = Task(self.tw, description="test", project="random")
96         task.save()
97
98         filtered_task = self.tw.tasks.get(project="random")
99         self.assertEqual(filtered_task['project'], "random")
100
101
102 class TaskTest(TasklibTest):
103
104     def test_create_unsaved_task(self):
105         # Make sure a new task is not saved unless explicitly called for
106         t = Task(self.tw, description="test task")
107         self.assertEqual(len(self.tw.tasks.all()), 0)
108
109     # TODO: once python 2.6 compatiblity is over, use context managers here
110     #       and in all subsequent tests for assertRaises
111
112     def test_delete_unsaved_task(self):
113         t = Task(self.tw, description="test task")
114         self.assertRaises(Task.NotSaved, t.delete)
115
116     def test_complete_unsaved_task(self):
117         t = Task(self.tw, description="test task")
118         self.assertRaises(Task.NotSaved, t.done)
119
120     def test_refresh_unsaved_task(self):
121         t = Task(self.tw, description="test task")
122         self.assertRaises(Task.NotSaved, t.refresh)
123
124     def test_delete_deleted_task(self):
125         t = Task(self.tw, description="test task")
126         t.save()
127         t.delete()
128
129         self.assertRaises(Task.DeletedTask, t.delete)
130
131     def test_complete_completed_task(self):
132         t = Task(self.tw, description="test task")
133         t.save()
134         t.done()
135
136         self.assertRaises(Task.CompletedTask, t.done)
137
138     def test_complete_deleted_task(self):
139         t = Task(self.tw, description="test task")
140         t.save()
141         t.delete()
142
143         self.assertRaises(Task.DeletedTask, t.done)
144
145     def test_modify_simple_attribute_without_space(self):
146         t = Task(self.tw, description="test")
147         t.save()
148
149         self.assertEquals(t['description'], "test")
150
151         t['description'] = "test-modified"
152         t.save()
153
154         self.assertEquals(t['description'], "test-modified")
155
156     def test_modify_simple_attribute_with_space(self):
157         # Space can pose problems with parsing
158         t = Task(self.tw, description="test task")
159         t.save()
160
161         self.assertEquals(t['description'], "test task")
162
163         t['description'] = "test task modified"
164         t.save()
165
166         self.assertEquals(t['description'], "test task modified")
167
168     def test_empty_dependency_set_of_unsaved_task(self):
169         t = Task(self.tw, description="test task")
170         self.assertEqual(t['depends'], set())
171
172     def test_empty_dependency_set_of_saved_task(self):
173         t = Task(self.tw, description="test task")
174         t.save()
175         self.assertEqual(t['depends'], set())
176
177     def test_set_unsaved_task_as_dependency(self):
178         # Adds only one dependency to task with no dependencies
179         t = Task(self.tw, description="test task")
180         dependency = Task(self.tw, description="needs to be done first")
181
182         # We only save the parent task, dependency task is unsaved
183         t.save()
184
185         self.assertRaises(Task.NotSaved,
186                           t.__setitem__, 'depends', set([dependency]))
187
188     def test_set_simple_dependency_set(self):
189         # Adds only one dependency to task with no dependencies
190         t = Task(self.tw, description="test task")
191         dependency = Task(self.tw, description="needs to be done first")
192
193         t.save()
194         dependency.save()
195
196         t['depends'] = set([dependency])
197
198         self.assertEqual(t['depends'], set([dependency]))
199
200     def test_set_complex_dependency_set(self):
201         # Adds two dependencies to task with no dependencies
202         t = Task(self.tw, description="test task")
203         dependency1 = Task(self.tw, description="needs to be done first")
204         dependency2 = Task(self.tw, description="needs to be done second")
205
206         t.save()
207         dependency1.save()
208         dependency2.save()
209
210         t['depends'] = set([dependency1, dependency2])
211
212         self.assertEqual(t['depends'], set([dependency1, dependency2]))
213
214     def test_remove_from_dependency_set(self):
215         # Removes dependency from task with two dependencies
216         t = Task(self.tw, description="test task")
217         dependency1 = Task(self.tw, description="needs to be done first")
218         dependency2 = Task(self.tw, description="needs to be done second")
219
220         dependency1.save()
221         dependency2.save()
222
223         t['depends'] = set([dependency1, dependency2])
224         t.save()
225
226         t['depends'] = t['depends'] - set([dependency2])
227         t.save()
228
229         self.assertEqual(t['depends'], set([dependency1]))
230
231     def test_add_to_dependency_set(self):
232         # Adds dependency to task with one dependencies
233         t = Task(self.tw, description="test task")
234         dependency1 = Task(self.tw, description="needs to be done first")
235         dependency2 = Task(self.tw, description="needs to be done second")
236
237         dependency1.save()
238         dependency2.save()
239
240         t['depends'] = set([dependency1])
241         t.save()
242
243         t['depends'] = t['depends'] | set([dependency2])
244         t.save()
245
246         self.assertEqual(t['depends'], set([dependency1, dependency2]))
247
248     def test_simple_dependency_set_save_repeatedly(self):
249         # Adds only one dependency to task with no dependencies
250         t = Task(self.tw, description="test task")
251         dependency = Task(self.tw, description="needs to be done first")
252         dependency.save()
253
254         t['depends'] = set([dependency])
255         t.save()
256
257         # We taint the task, but keep depends intact
258         t['description'] = "test task modified"
259         t.save()
260
261         self.assertEqual(t['depends'], set([dependency]))
262
263         # We taint the task, but assign the same set to the depends
264         t['depends'] = set([dependency])
265         t['description'] = "test task modified again"
266         t.save()
267
268         self.assertEqual(t['depends'], set([dependency]))
269
270     def test_compare_different_tasks(self):
271         # Negative: compare two different tasks
272         t1 = Task(self.tw, description="test task")
273         t2 = Task(self.tw, description="test task")
274
275         t1.save()
276         t2.save()
277
278         self.assertEqual(t1 == t2, False)
279
280     def test_compare_same_task_object(self):
281         # Compare Task object wit itself
282         t = Task(self.tw, description="test task")
283         t.save()
284
285         self.assertEqual(t == t, True)
286
287     def test_compare_same_task(self):
288         # Compare the same task using two different objects
289         t1 = Task(self.tw, description="test task")
290         t1.save()
291
292         t2 = self.tw.tasks.get(uuid=t1['uuid'])
293         self.assertEqual(t1 == t2, True)
294
295     def test_compare_unsaved_tasks(self):
296         # t1 and t2 are unsaved tasks, considered to be unequal
297         # despite the content of data
298         t1 = Task(self.tw, description="test task")
299         t2 = Task(self.tw, description="test task")
300
301         self.assertEqual(t1 == t2, False)
302
303     def test_hash_unsaved_tasks(self):
304         # Considered equal, it's the same object
305         t1 = Task(self.tw, description="test task")
306         t2 = t1
307         self.assertEqual(hash(t1) == hash(t2), True)
308
309     def test_hash_same_task(self):
310         # Compare the hash of the task using two different objects
311         t1 = Task(self.tw, description="test task")
312         t1.save()
313
314         t2 = self.tw.tasks.get(uuid=t1['uuid'])
315         self.assertEqual(t1.__hash__(), t2.__hash__())
316
317     def test_adding_task_with_priority(self):
318         t = Task(self.tw, description="test task", priority="M")
319         t.save()
320
321     def test_removing_priority_with_none(self):
322         t = Task(self.tw, description="test task", priority="L")
323         t.save()
324
325         # Remove the priority mark
326         t['priority'] = None
327         t.save()
328
329         # Assert that priority is not there after saving
330         self.assertEqual(t['priority'], None)
331
332     def test_adding_task_with_due_time(self):
333         t = Task(self.tw, description="test task", due=datetime.datetime.now())
334         t.save()
335
336     def test_removing_due_time_with_none(self):
337         t = Task(self.tw, description="test task", due=datetime.datetime.now())
338         t.save()
339
340         # Remove the due timestamp
341         t['due'] = None
342         t.save()
343
344         # Assert that due timestamp is no longer there
345         self.assertEqual(t['due'], None)
346
347     def test_modified_fields_new_task(self):
348         t = Task(self.tw)
349
350         # This should be empty with new task
351         self.assertEqual(set(t._modified_fields), set())
352
353         # Modify the task
354         t['description'] = "test task"
355         self.assertEqual(set(t._modified_fields), set(['description']))
356
357         t['due'] = datetime.datetime(2014, 2, 14, 14, 14, 14)  # <3
358         self.assertEqual(set(t._modified_fields), set(['description', 'due']))
359
360         t['project'] = "test project"
361         self.assertEqual(set(t._modified_fields),
362                          set(['description', 'due', 'project']))
363
364         # List of modified fields should clear out when saved
365         t.save()
366         self.assertEqual(set(t._modified_fields), set())
367
368         # Reassigning the fields with the same values now should not produce
369         # modified fields
370         t['description'] = "test task"
371         t['due'] = datetime.datetime(2014, 2, 14, 14, 14, 14)  # <3
372         t['project'] = "test project"
373         self.assertEqual(set(t._modified_fields), set())
374
375     def test_modified_fields_loaded_task(self):
376         t = Task(self.tw)
377
378         # Modify the task
379         t['description'] = "test task"
380         t['due'] = datetime.datetime(2014, 2, 14, 14, 14, 14)  # <3
381         t['project'] = "test project"
382
383         dependency = Task(self.tw, description="dependency")
384         dependency.save()
385         t['depends'] = set([dependency])
386
387         # List of modified fields should clear out when saved
388         t.save()
389         self.assertEqual(set(t._modified_fields), set())
390
391         # Get the task by using a filter by UUID
392         t2 = self.tw.tasks.get(uuid=t['uuid'])
393
394         # Reassigning the fields with the same values now should not produce
395         # modified fields
396         t['description'] = "test task"
397         t['due'] = datetime.datetime(2014, 2, 14, 14, 14, 14)  # <3
398         t['project'] = "test project"
399         t['depends'] = set([dependency])
400         self.assertEqual(set(t._modified_fields), set())
401
402     def test_setting_read_only_attrs_through_init(self):
403         # Test that we are unable to set readonly attrs through __init__
404         for readonly_key in Task.read_only_fields:
405             kwargs = {'description': 'test task', readonly_key: 'value'}
406             self.assertRaises(RuntimeError,
407                               lambda: Task(self.tw, **kwargs))
408
409     def test_setting_read_only_attrs_through_setitem(self):
410         # Test that we are unable to set readonly attrs through __init__
411         for readonly_key in Task.read_only_fields:
412             t = Task(self.tw, description='test task')
413             self.assertRaises(RuntimeError,
414                               lambda: t.__setitem__(readonly_key, 'value'))
415
416
417 class AnnotationTest(TasklibTest):
418
419     def setUp(self):
420         super(AnnotationTest, self).setUp()
421         Task(self.tw, description="test task").save()
422
423     def test_adding_annotation(self):
424         task = self.tw.tasks.get()
425         task.add_annotation('test annotation')
426         self.assertEqual(len(task['annotations']), 1)
427         ann = task['annotations'][0]
428         self.assertEqual(ann['description'], 'test annotation')
429
430     def test_removing_annotation(self):
431         task = self.tw.tasks.get()
432         task.add_annotation('test annotation')
433         ann = task['annotations'][0]
434         ann.remove()
435         self.assertEqual(len(task['annotations']), 0)
436
437     def test_removing_annotation_by_description(self):
438         task = self.tw.tasks.get()
439         task.add_annotation('test annotation')
440         task.remove_annotation('test annotation')
441         self.assertEqual(len(task['annotations']), 0)
442
443     def test_removing_annotation_by_obj(self):
444         task = self.tw.tasks.get()
445         task.add_annotation('test annotation')
446         ann = task['annotations'][0]
447         task.remove_annotation(ann)
448         self.assertEqual(len(task['annotations']), 0)
449
450
451 class UnicodeTest(TasklibTest):
452
453     def test_unicode_task(self):
454         Task(self.tw, description="†åßk").save()
455         self.tw.tasks.get()
456
457     def test_non_unicode_task(self):
458         Task(self.tw, description="test task").save()
459         self.tw.tasks.get()