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

Task: Add support for creating Task objects in hook scripts
[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         t['depends'] = set([dependency])
185
186         self.assertRaises(Task.NotSaved, t.save)
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'].remove(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'].add(dependency2)
244         t.save()
245
246         self.assertEqual(t['depends'], set([dependency1, dependency2]))
247
248     def test_add_to_empty_dependency_set(self):
249         # Adds dependency to task with one dependencies
250         t = Task(self.tw, description="test task")
251         dependency = Task(self.tw, description="needs to be done first")
252
253         dependency.save()
254
255         t['depends'].add(dependency)
256         t.save()
257
258         self.assertEqual(t['depends'], set([dependency]))
259
260     def test_simple_dependency_set_save_repeatedly(self):
261         # Adds only one dependency to task with no dependencies
262         t = Task(self.tw, description="test task")
263         dependency = Task(self.tw, description="needs to be done first")
264         dependency.save()
265
266         t['depends'] = set([dependency])
267         t.save()
268
269         # We taint the task, but keep depends intact
270         t['description'] = "test task modified"
271         t.save()
272
273         self.assertEqual(t['depends'], set([dependency]))
274
275         # We taint the task, but assign the same set to the depends
276         t['depends'] = set([dependency])
277         t['description'] = "test task modified again"
278         t.save()
279
280         self.assertEqual(t['depends'], set([dependency]))
281
282     def test_compare_different_tasks(self):
283         # Negative: compare two different tasks
284         t1 = Task(self.tw, description="test task")
285         t2 = Task(self.tw, description="test task")
286
287         t1.save()
288         t2.save()
289
290         self.assertEqual(t1 == t2, False)
291
292     def test_compare_same_task_object(self):
293         # Compare Task object wit itself
294         t = Task(self.tw, description="test task")
295         t.save()
296
297         self.assertEqual(t == t, True)
298
299     def test_compare_same_task(self):
300         # Compare the same task using two different objects
301         t1 = Task(self.tw, description="test task")
302         t1.save()
303
304         t2 = self.tw.tasks.get(uuid=t1['uuid'])
305         self.assertEqual(t1 == t2, True)
306
307     def test_compare_unsaved_tasks(self):
308         # t1 and t2 are unsaved tasks, considered to be unequal
309         # despite the content of data
310         t1 = Task(self.tw, description="test task")
311         t2 = Task(self.tw, description="test task")
312
313         self.assertEqual(t1 == t2, False)
314
315     def test_hash_unsaved_tasks(self):
316         # Considered equal, it's the same object
317         t1 = Task(self.tw, description="test task")
318         t2 = t1
319         self.assertEqual(hash(t1) == hash(t2), True)
320
321     def test_hash_same_task(self):
322         # Compare the hash of the task using two different objects
323         t1 = Task(self.tw, description="test task")
324         t1.save()
325
326         t2 = self.tw.tasks.get(uuid=t1['uuid'])
327         self.assertEqual(t1.__hash__(), t2.__hash__())
328
329     def test_adding_task_with_priority(self):
330         t = Task(self.tw, description="test task", priority="M")
331         t.save()
332
333     def test_removing_priority_with_none(self):
334         t = Task(self.tw, description="test task", priority="L")
335         t.save()
336
337         # Remove the priority mark
338         t['priority'] = None
339         t.save()
340
341         # Assert that priority is not there after saving
342         self.assertEqual(t['priority'], None)
343
344     def test_adding_task_with_due_time(self):
345         t = Task(self.tw, description="test task", due=datetime.datetime.now())
346         t.save()
347
348     def test_removing_due_time_with_none(self):
349         t = Task(self.tw, description="test task", due=datetime.datetime.now())
350         t.save()
351
352         # Remove the due timestamp
353         t['due'] = None
354         t.save()
355
356         # Assert that due timestamp is no longer there
357         self.assertEqual(t['due'], None)
358
359     def test_modified_fields_new_task(self):
360         t = Task(self.tw)
361
362         # This should be empty with new task
363         self.assertEqual(set(t._modified_fields), set())
364
365         # Modify the task
366         t['description'] = "test task"
367         self.assertEqual(set(t._modified_fields), set(['description']))
368
369         t['due'] = datetime.datetime(2014, 2, 14, 14, 14, 14)  # <3
370         self.assertEqual(set(t._modified_fields), set(['description', 'due']))
371
372         t['project'] = "test project"
373         self.assertEqual(set(t._modified_fields),
374                          set(['description', 'due', 'project']))
375
376         # List of modified fields should clear out when saved
377         t.save()
378         self.assertEqual(set(t._modified_fields), set())
379
380         # Reassigning the fields with the same values now should not produce
381         # modified fields
382         t['description'] = "test task"
383         t['due'] = datetime.datetime(2014, 2, 14, 14, 14, 14)  # <3
384         t['project'] = "test project"
385         self.assertEqual(set(t._modified_fields), set())
386
387     def test_modified_fields_loaded_task(self):
388         t = Task(self.tw)
389
390         # Modify the task
391         t['description'] = "test task"
392         t['due'] = datetime.datetime(2014, 2, 14, 14, 14, 14)  # <3
393         t['project'] = "test project"
394
395         dependency = Task(self.tw, description="dependency")
396         dependency.save()
397         t['depends'] = set([dependency])
398
399         # List of modified fields should clear out when saved
400         t.save()
401         self.assertEqual(set(t._modified_fields), set())
402
403         # Get the task by using a filter by UUID
404         t2 = self.tw.tasks.get(uuid=t['uuid'])
405
406         # Reassigning the fields with the same values now should not produce
407         # modified fields
408         t['description'] = "test task"
409         t['due'] = datetime.datetime(2014, 2, 14, 14, 14, 14)  # <3
410         t['project'] = "test project"
411         t['depends'] = set([dependency])
412         self.assertEqual(set(t._modified_fields), set())
413
414     def test_setting_read_only_attrs_through_init(self):
415         # Test that we are unable to set readonly attrs through __init__
416         for readonly_key in Task.read_only_fields:
417             kwargs = {'description': 'test task', readonly_key: 'value'}
418             self.assertRaises(RuntimeError,
419                               lambda: Task(self.tw, **kwargs))
420
421     def test_setting_read_only_attrs_through_setitem(self):
422         # Test that we are unable to set readonly attrs through __init__
423         for readonly_key in Task.read_only_fields:
424             t = Task(self.tw, description='test task')
425             self.assertRaises(RuntimeError,
426                               lambda: t.__setitem__(readonly_key, 'value'))
427
428     def test_saving_unmodified_task(self):
429         t = Task(self.tw, description="test task")
430         t.save()
431         t.save()
432
433     def test_adding_tag_by_appending(self):
434         t = Task(self.tw, description="test task", tags=['test1'])
435         t.save()
436         t['tags'].append('test2')
437         t.save()
438         self.assertEqual(t['tags'], ['test1', 'test2'])
439
440     def test_adding_tag_by_appending_empty(self):
441         t = Task(self.tw, description="test task")
442         t.save()
443         t['tags'].append('test')
444         t.save()
445         self.assertEqual(t['tags'], ['test'])
446
447
448 class AnnotationTest(TasklibTest):
449
450     def setUp(self):
451         super(AnnotationTest, self).setUp()
452         Task(self.tw, description="test task").save()
453
454     def test_adding_annotation(self):
455         task = self.tw.tasks.get()
456         task.add_annotation('test annotation')
457         self.assertEqual(len(task['annotations']), 1)
458         ann = task['annotations'][0]
459         self.assertEqual(ann['description'], 'test annotation')
460
461     def test_removing_annotation(self):
462         task = self.tw.tasks.get()
463         task.add_annotation('test annotation')
464         ann = task['annotations'][0]
465         ann.remove()
466         self.assertEqual(len(task['annotations']), 0)
467
468     def test_removing_annotation_by_description(self):
469         task = self.tw.tasks.get()
470         task.add_annotation('test annotation')
471         task.remove_annotation('test annotation')
472         self.assertEqual(len(task['annotations']), 0)
473
474     def test_removing_annotation_by_obj(self):
475         task = self.tw.tasks.get()
476         task.add_annotation('test annotation')
477         ann = task['annotations'][0]
478         task.remove_annotation(ann)
479         self.assertEqual(len(task['annotations']), 0)
480
481     def test_annotation_after_modification(self):
482          task = self.tw.tasks.get()
483          task['project'] = 'test'
484          task.add_annotation('I should really do this task')
485          self.assertEqual(task['project'], 'test')
486          task.save()
487          self.assertEqual(task['project'], 'test')
488
489
490 class UnicodeTest(TasklibTest):
491
492     def test_unicode_task(self):
493         Task(self.tw, description="†åßk").save()
494         self.tw.tasks.get()
495
496     def test_non_unicode_task(self):
497         Task(self.tw, description="test task").save()
498         self.tw.tasks.get()