PageRenderTime 47ms CodeModel.GetById 18ms RepoModel.GetById 0ms app.codeStats 0ms

/MobileGTD/specs/model/project_spec.py

http://mobilegtd.googlecode.com/
Python | 326 lines | 214 code | 89 blank | 23 comment | 9 complexity | 518f5035124270687acaa7ff32ea9089 MD5 | raw file
  1. import unittest
  2. from mock import Mock
  3. import model.project
  4. from model.project import Project
  5. from model import project
  6. from model import action
  7. class ProjectClassBehaviour(unittest.TestCase):
  8. def setUp(self):
  9. self.p_class = Project
  10. self.observer = Mock()
  11. self.p_class.observers.append(self.observer)
  12. def test_should_inform_listeners_of_project_creation(self):
  13. p = Project(u'Test')
  14. self.assertTrue(self.observer.notify.called)
  15. class ProjectBehaviour(unittest.TestCase):
  16. def setUp(self):
  17. self.name = u'my project'
  18. self.status = self.initial_status()
  19. self.actions = self.initial_actions()
  20. self.infos = self.initial_infos()
  21. self.project = model.project.Project(self.name,self.status)
  22. for a in self.actions:
  23. self.project.add_action(a)
  24. for i in self.infos:
  25. self.project.add_info(i)
  26. self.observer = Mock()
  27. self.project.observers.append(self.observer)
  28. def initial_actions(self):
  29. return []
  30. def create_action(self):
  31. a = Mock()
  32. a.status = self.action_status()
  33. return a
  34. def action_status(self):
  35. return None
  36. def initial_infos(self):
  37. return []
  38. def initial_status(self):
  39. return project.inactive
  40. def test_should_remember_its_name(self):
  41. self.assertEqual(self.project.name,self.name)
  42. def test_should_notify_observer_of_name_change(self):
  43. self.project.name = 'new name'
  44. self.assert_observed('name','new name',self.name)
  45. def test_should_notify_observer_of_status_change(self):
  46. old_status = self.project.status
  47. self.project.status = project.done
  48. self.assert_observed_status(project.done,old_status)
  49. def create_action_with_status(self,status=action.inactive):
  50. a = Mock()
  51. a.status=status
  52. return a
  53. def test_should_register_itself_as_project_for_added_actions(self):
  54. a = self.create_action_with_status()
  55. self.project.add_action(a)
  56. self.assertEqual(a.project,self.project)
  57. def test_should_register_itself_as_observer_for_added_actions(self):
  58. a = self.create_action()
  59. self.project.add_action(a)
  60. a.observers.append.assert_called_with(self.project)
  61. def test_should_notify_observer_of_added_actions(self):
  62. a = Mock()
  63. a.status = action.done
  64. self.project.add_action(a)
  65. self.assert_observed('add_action', a)
  66. def test_should_notify_observers_of_action_status_changes(self):
  67. for a in self.project.actions:
  68. self.project.action_changed_status(a, action.active)
  69. self.assert_observed('changed_action', a, None)
  70. def test_should_set_added_unprocessed_actions_to_active(self):
  71. a = Mock()
  72. a.status = action.unprocessed
  73. self.project.add_action(a)
  74. self.assertEqual(a.status,action.active)
  75. def test_should_be_equal_if_name_and_status_are_identical(self):
  76. other = Mock()
  77. other.name = self.project.name
  78. other.status = self.project.status
  79. self.assertTrue(self.project == other)
  80. self.assertFalse(self.project != other)
  81. other.name = 'other name'
  82. self.assertTrue(self.project != other)
  83. def assert_observed(self,attribute,new=None,old=None):
  84. calls = self.observer.notify.call_args_list
  85. self.assertTrue(((self.project,attribute),{'new':new,'old':old}) in calls,
  86. 'Expected notification from %s concerning the change of %s from %s to %s\n Only got these calls:\n%s'%(repr(self.project),repr(attribute),repr(old),repr(new),repr(calls)))
  87. def assert_observed_status(self,status,previous_status=None):
  88. if not previous_status:
  89. previous_status = self.status
  90. self.assert_status(status)
  91. self.assert_observed('status',status,previous_status)
  92. def assert_status(self,status):
  93. self.assertEqual(self.project.status,status)
  94. class ActiveProjectBehaviour(ProjectBehaviour):
  95. def initial_actions(self):
  96. a = self.create_action_with_status(action.active)
  97. return [a]
  98. def initial_status(self):
  99. return project.active
  100. def test_should_be_active(self):
  101. self.assert_status(project.active)
  102. def test_should_contain_active_actions(self):
  103. self.assertTrue(self.project.has_active_actions())
  104. # def test_should_become_inactive_if_no_active_action_remains(self):
  105. # self.project.status = project.active
  106. # for a in self.project.actions_with_status(action.active):
  107. # self.project.remove_action(a)
  108. # self.assert_observed_status(project.inactive)
  109. #
  110. # def test_should_become_inactive_when_active_actions_become_inactive(self):
  111. # self.project.status = project.active
  112. # for a in self.project.actions_with_status(action.active):
  113. # a.status = action.done
  114. # self.project.notify(a,'status',action.done)
  115. # self.assert_observed_status(project.inactive)
  116. def test_should_deactivate_its_active_actions_on_deactivate(self):
  117. active_actions = self.project.actions_with_status(action.active)
  118. self.project.deactivate()
  119. self.assertEqual(self.project.status,project.inactive)
  120. for a in active_actions:
  121. self.assertEqual(a.status,action.inactive)
  122. class InactiveProjectBehaviour(ProjectBehaviour):
  123. def initial_status(self):
  124. return project.inactive
  125. def test_should_be_inactive(self):
  126. self.assert_status(project.inactive)
  127. # def test_should_become_active_if_active_actions_are_added(self):
  128. # a = Mock()
  129. # a.status = action.active
  130. # self.project.add_action(a)
  131. # self.assertEqual(self.project.status,project.active)
  132. def test_should_activate_all_inactive_actions_when_activated_itself(self):
  133. inactive_actions = self.project.actions_with_status(action.inactive)
  134. for a in inactive_actions:
  135. self.assertEqual(a.status,action.inactive)
  136. self.project.activate()
  137. for a in inactive_actions:
  138. self.assertEqual(a.status,action.active)
  139. class EmptyProjectBehaviour(InactiveProjectBehaviour):
  140. def test_should_return_an_empty_list_of_actions(self):
  141. self.assertEqual(self.project.actions,[])
  142. def test_should_return_an_empty_list_of_infos(self):
  143. self.assertEqual(self.project.infos,[])
  144. class ProjectWithActionsBehaviour(ProjectBehaviour):
  145. def setUp(self):
  146. self.action = self.create_action()
  147. super(ProjectWithActionsBehaviour,self).setUp()
  148. def initial_actions(self):
  149. return [self.action]
  150. def test_should_contain_all_added_actions(self):
  151. self.assertEqual(self.project.actions,self.actions)
  152. def test_should_forget_removed_actions(self):
  153. self.project.remove_action(self.actions[0])
  154. self.assertFalse(self.actions[0] in self.project.actions)
  155. def test_should_remove_itself_as_observer_for_removed_actions(self):
  156. self.project.remove_action(self.actions[0])
  157. self.actions[0].observers.remove.assert_called_with(self.project)
  158. def test_should_set_action_to_done_before_removing(self):
  159. self.project.remove_action(self.actions[0])
  160. self.assertEqual(self.actions[0].status,action.done)
  161. def test_should_notify_observer_of_removed_actions(self):
  162. self.project.remove_action(self.actions[0])
  163. self.assert_observed('remove_action',self.actions[0],None)
  164. def test_generator(field):
  165. def test_should_notify_observer_of_changes_in_actions(self):
  166. self.project.notify(self.actions[0], field, 'new %s'%field)
  167. self.assert_observed('changed_action',self.actions[0])
  168. return test_should_notify_observer_of_changes_in_actions
  169. for field in ['description','info','context']:
  170. no_change_on_action_becoming_active = 'test_should_notify_observer_of_changes_in_action_%s' % field
  171. test = test_generator(field)
  172. setattr(ProjectWithActionsBehaviour, no_change_on_action_becoming_active, test)
  173. class ProjectWithInactiveActionsBehaviour(ProjectWithActionsBehaviour):
  174. def action_status(self):
  175. return action.inactive
  176. # def test_should_become_active_when_inactive_actions_become_active(self):
  177. # self.actions[0].status = action.active
  178. # self.project.notify(self.actions[0],'status',action.active)
  179. # self.assert_observed_status(project.active)
  180. def test_should_return_the_inactive_action(self):
  181. self.assertEqual(self.project.actions_with_status(action.inactive),[self.actions[0]])
  182. def test_should_return_no_active_action(self):
  183. self.assertEqual(self.project.actions_with_status(action.active),[])
  184. class InactiveProjectWithInactiveActionsBehaviour(ProjectWithInactiveActionsBehaviour,InactiveProjectBehaviour):
  185. pass
  186. class ProjectWithActiveActionsBehaviour(ProjectWithActionsBehaviour):
  187. def action_status(self):
  188. return action.active
  189. def test_should_return_the_active_action(self):
  190. self.assertEqual(self.project.actions_with_status(action.active),[self.actions[0]])
  191. def test_should_return_no_inactive_action(self):
  192. self.assertEqual(self.project.actions_with_status(action.inactive),[])
  193. #class ActiveProjectWithActiveActionsBehaviour(ProjectWithActiveActionsBehaviour,ActiveProjectBehaviour):
  194. class ProjectWithInfosBehaviour(ProjectBehaviour):
  195. def setUp(self):
  196. super(ProjectWithInfosBehaviour,self).setUp()
  197. self.info = Mock()
  198. self.project.add_info(self.info)
  199. def test_should_contain_all_added_infos(self):
  200. self.assertEqual(self.project.infos,[self.info])
  201. def test_should_really_forget_removed_infos(self):
  202. self.project.remove_info(self.info)
  203. self.assertFalse(self.info in self.project.infos)
  204. def test_should_register_itself_as_observer_for_added_infos(self):
  205. self.info.observers.append.assert_called_with(self.project)
  206. def test_should_deregister_itself_as_observer_for_removed_infos(self):
  207. self.project.remove_info(self.info)
  208. self.info.observers.remove.assert_called_with(self.project)
  209. def test_should_notify_observer_of_removed_infos(self):
  210. self.project.remove_info(self.info)
  211. self.assert_observed('remove_info',self.info)
  212. def test_should_notify_observer_of_changes_in_infos(self):
  213. self.project.notify(self.info, 'text', 'new text')
  214. self.assert_observed('changed_info',self.info)
  215. def generate_no_becoming_active_test(status):
  216. def initial_status(self):
  217. return status
  218. def test_should_not_change_status_if_actions_become_active(self):
  219. self.project.notify(self.actions[0], 'status', action.active)
  220. self.assertEqual(status,self.project.status)
  221. def test_should_not_change_status_if_active_actions_are_added(self):
  222. a = Mock()
  223. a.status = action.active
  224. self.project.add_action(a)
  225. self.assertEqual(status,self.project.status)
  226. return (initial_status,test_should_not_change_status_if_actions_become_active,test_should_not_change_status_if_active_actions_are_added)
  227. #
  228. for status in ['someday','done','tickled']:
  229. class_name = '%sProjectBehaviour'%status.capitalize()
  230. my_class=type(class_name,(ProjectWithActiveActionsBehaviour,),{})
  231. no_change_on_action_becoming_active = 'test_should_not_change_%s_status_if_actions_become_active' % status
  232. no_change_on_active_action_added= 'test_should_not_change_%s_status_if_active_actions_are_added' % status
  233. initial_status,no_change_on_action_becoming_active_test,no_change_on_active_action_added_test = generate_no_becoming_active_test(getattr(model.project,status))
  234. setattr(my_class, 'initial_status', initial_status)
  235. setattr(my_class, no_change_on_action_becoming_active, no_change_on_action_becoming_active_test)
  236. setattr(my_class, no_change_on_active_action_added, no_change_on_active_action_added_test)
  237. globals()[class_name]=my_class