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