telvue_state_machine 1.2.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (307) hide show
  1. checksums.yaml +7 -0
  2. data/.gitignore +8 -0
  3. data/.travis.yml +72 -0
  4. data/.yardopts +5 -0
  5. data/Appraisals +491 -0
  6. data/CHANGELOG.md +502 -0
  7. data/Gemfile +3 -0
  8. data/LICENSE +20 -0
  9. data/README.md +1263 -0
  10. data/Rakefile +41 -0
  11. data/examples/AutoShop_state.png +0 -0
  12. data/examples/Car_state.png +0 -0
  13. data/examples/Gemfile +5 -0
  14. data/examples/Gemfile.lock +14 -0
  15. data/examples/TrafficLight_state.png +0 -0
  16. data/examples/Vehicle_state.png +0 -0
  17. data/examples/auto_shop.rb +13 -0
  18. data/examples/car.rb +21 -0
  19. data/examples/doc/AutoShop.html +2856 -0
  20. data/examples/doc/AutoShop_state.png +0 -0
  21. data/examples/doc/Car.html +919 -0
  22. data/examples/doc/Car_state.png +0 -0
  23. data/examples/doc/TrafficLight.html +2230 -0
  24. data/examples/doc/TrafficLight_state.png +0 -0
  25. data/examples/doc/Vehicle.html +7921 -0
  26. data/examples/doc/Vehicle_state.png +0 -0
  27. data/examples/doc/_index.html +136 -0
  28. data/examples/doc/class_list.html +47 -0
  29. data/examples/doc/css/common.css +1 -0
  30. data/examples/doc/css/full_list.css +55 -0
  31. data/examples/doc/css/style.css +322 -0
  32. data/examples/doc/file_list.html +46 -0
  33. data/examples/doc/frames.html +13 -0
  34. data/examples/doc/index.html +136 -0
  35. data/examples/doc/js/app.js +205 -0
  36. data/examples/doc/js/full_list.js +173 -0
  37. data/examples/doc/js/jquery.js +16 -0
  38. data/examples/doc/method_list.html +734 -0
  39. data/examples/doc/top-level-namespace.html +105 -0
  40. data/examples/merb-rest/controller.rb +51 -0
  41. data/examples/merb-rest/model.rb +28 -0
  42. data/examples/merb-rest/view_edit.html.erb +24 -0
  43. data/examples/merb-rest/view_index.html.erb +23 -0
  44. data/examples/merb-rest/view_new.html.erb +13 -0
  45. data/examples/merb-rest/view_show.html.erb +17 -0
  46. data/examples/rails-rest/controller.rb +43 -0
  47. data/examples/rails-rest/migration.rb +7 -0
  48. data/examples/rails-rest/model.rb +23 -0
  49. data/examples/rails-rest/view__form.html.erb +34 -0
  50. data/examples/rails-rest/view_edit.html.erb +6 -0
  51. data/examples/rails-rest/view_index.html.erb +25 -0
  52. data/examples/rails-rest/view_new.html.erb +5 -0
  53. data/examples/rails-rest/view_show.html.erb +19 -0
  54. data/examples/traffic_light.rb +9 -0
  55. data/examples/vehicle.rb +33 -0
  56. data/gemfiles/active_model_3.0.0.gemfile +7 -0
  57. data/gemfiles/active_model_3.0.0.gemfile.lock +35 -0
  58. data/gemfiles/active_model_3.0.5.gemfile +7 -0
  59. data/gemfiles/active_model_3.0.5.gemfile.lock +35 -0
  60. data/gemfiles/active_model_3.1.1.gemfile +7 -0
  61. data/gemfiles/active_model_3.1.1.gemfile.lock +36 -0
  62. data/gemfiles/active_model_3.2.1.gemfile +7 -0
  63. data/gemfiles/active_model_3.2.12.gemfile +7 -0
  64. data/gemfiles/active_model_3.2.12.gemfile.lock +36 -0
  65. data/gemfiles/active_model_3.2.13.rc1.gemfile +7 -0
  66. data/gemfiles/active_model_3.2.13.rc1.gemfile.lock +36 -0
  67. data/gemfiles/active_model_4.0.0.gemfile +9 -0
  68. data/gemfiles/active_model_4.0.0.gemfile.lock +78 -0
  69. data/gemfiles/active_record_2.0.0.gemfile +9 -0
  70. data/gemfiles/active_record_2.0.0.gemfile.lock +39 -0
  71. data/gemfiles/active_record_2.0.5.gemfile +9 -0
  72. data/gemfiles/active_record_2.0.5.gemfile.lock +39 -0
  73. data/gemfiles/active_record_2.1.0.gemfile +9 -0
  74. data/gemfiles/active_record_2.1.0.gemfile.lock +39 -0
  75. data/gemfiles/active_record_2.1.2.gemfile +9 -0
  76. data/gemfiles/active_record_2.1.2.gemfile.lock +39 -0
  77. data/gemfiles/active_record_2.2.3.gemfile +9 -0
  78. data/gemfiles/active_record_2.2.3.gemfile.lock +39 -0
  79. data/gemfiles/active_record_2.3.12.gemfile +9 -0
  80. data/gemfiles/active_record_2.3.12.gemfile.lock +39 -0
  81. data/gemfiles/active_record_2.3.5.gemfile +9 -0
  82. data/gemfiles/active_record_2.3.5.gemfile.lock +39 -0
  83. data/gemfiles/active_record_3.0.0.gemfile +9 -0
  84. data/gemfiles/active_record_3.0.0.gemfile.lock +51 -0
  85. data/gemfiles/active_record_3.0.5.gemfile +9 -0
  86. data/gemfiles/active_record_3.0.5.gemfile.lock +50 -0
  87. data/gemfiles/active_record_3.1.1.gemfile +9 -0
  88. data/gemfiles/active_record_3.1.1.gemfile.lock +51 -0
  89. data/gemfiles/active_record_3.2.12.gemfile +9 -0
  90. data/gemfiles/active_record_3.2.12.gemfile.lock +51 -0
  91. data/gemfiles/active_record_3.2.13.rc1.gemfile +9 -0
  92. data/gemfiles/active_record_3.2.13.rc1.gemfile.lock +51 -0
  93. data/gemfiles/active_record_4.0.0.gemfile +11 -0
  94. data/gemfiles/active_record_4.0.0.gemfile.lock +83 -0
  95. data/gemfiles/data_mapper_0.10.2.gemfile +13 -0
  96. data/gemfiles/data_mapper_0.10.2.gemfile.lock +56 -0
  97. data/gemfiles/data_mapper_0.9.11.gemfile +13 -0
  98. data/gemfiles/data_mapper_0.9.11.gemfile.lock +71 -0
  99. data/gemfiles/data_mapper_0.9.4.gemfile +12 -0
  100. data/gemfiles/data_mapper_0.9.4.gemfile.lock +70 -0
  101. data/gemfiles/data_mapper_0.9.7.gemfile +13 -0
  102. data/gemfiles/data_mapper_0.9.7.gemfile.lock +67 -0
  103. data/gemfiles/data_mapper_1.0.0.gemfile +12 -0
  104. data/gemfiles/data_mapper_1.0.0.gemfile.lock +63 -0
  105. data/gemfiles/data_mapper_1.0.1.gemfile +12 -0
  106. data/gemfiles/data_mapper_1.0.1.gemfile.lock +63 -0
  107. data/gemfiles/data_mapper_1.0.2.gemfile +12 -0
  108. data/gemfiles/data_mapper_1.0.2.gemfile.lock +63 -0
  109. data/gemfiles/data_mapper_1.1.0.gemfile +12 -0
  110. data/gemfiles/data_mapper_1.1.0.gemfile.lock +61 -0
  111. data/gemfiles/data_mapper_1.2.0.gemfile +12 -0
  112. data/gemfiles/data_mapper_1.2.0.gemfile.lock +61 -0
  113. data/gemfiles/default.gemfile +7 -0
  114. data/gemfiles/default.gemfile.lock +27 -0
  115. data/gemfiles/graphviz_0.9.17.gemfile +7 -0
  116. data/gemfiles/graphviz_0.9.17.gemfile.lock +29 -0
  117. data/gemfiles/graphviz_0.9.21.gemfile +7 -0
  118. data/gemfiles/graphviz_0.9.21.gemfile.lock +29 -0
  119. data/gemfiles/graphviz_1.0.0.gemfile +7 -0
  120. data/gemfiles/graphviz_1.0.0.gemfile.lock +29 -0
  121. data/gemfiles/graphviz_1.0.3.gemfile +7 -0
  122. data/gemfiles/graphviz_1.0.3.gemfile.lock +29 -0
  123. data/gemfiles/graphviz_1.0.8.gemfile +7 -0
  124. data/gemfiles/graphviz_1.0.8.gemfile.lock +29 -0
  125. data/gemfiles/mongo_mapper_0.10.0.gemfile +8 -0
  126. data/gemfiles/mongo_mapper_0.10.0.gemfile.lock +47 -0
  127. data/gemfiles/mongo_mapper_0.11.2.gemfile +9 -0
  128. data/gemfiles/mongo_mapper_0.11.2.gemfile.lock +48 -0
  129. data/gemfiles/mongo_mapper_0.12.0.gemfile +9 -0
  130. data/gemfiles/mongo_mapper_0.12.0.gemfile.lock +48 -0
  131. data/gemfiles/mongo_mapper_0.5.5.gemfile +8 -0
  132. data/gemfiles/mongo_mapper_0.5.5.gemfile.lock +36 -0
  133. data/gemfiles/mongo_mapper_0.5.8.gemfile +8 -0
  134. data/gemfiles/mongo_mapper_0.5.8.gemfile.lock +36 -0
  135. data/gemfiles/mongo_mapper_0.6.0.gemfile +8 -0
  136. data/gemfiles/mongo_mapper_0.6.0.gemfile.lock +36 -0
  137. data/gemfiles/mongo_mapper_0.6.10.gemfile +8 -0
  138. data/gemfiles/mongo_mapper_0.6.10.gemfile.lock +36 -0
  139. data/gemfiles/mongo_mapper_0.7.0.gemfile +8 -0
  140. data/gemfiles/mongo_mapper_0.7.0.gemfile.lock +36 -0
  141. data/gemfiles/mongo_mapper_0.7.5.gemfile +8 -0
  142. data/gemfiles/mongo_mapper_0.7.5.gemfile.lock +39 -0
  143. data/gemfiles/mongo_mapper_0.8.0.gemfile +10 -0
  144. data/gemfiles/mongo_mapper_0.8.0.gemfile.lock +43 -0
  145. data/gemfiles/mongo_mapper_0.8.3.gemfile +10 -0
  146. data/gemfiles/mongo_mapper_0.8.3.gemfile.lock +43 -0
  147. data/gemfiles/mongo_mapper_0.8.4.gemfile +8 -0
  148. data/gemfiles/mongo_mapper_0.8.4.gemfile.lock +42 -0
  149. data/gemfiles/mongo_mapper_0.8.6.gemfile +8 -0
  150. data/gemfiles/mongo_mapper_0.8.6.gemfile.lock +42 -0
  151. data/gemfiles/mongo_mapper_0.9.0.gemfile +7 -0
  152. data/gemfiles/mongo_mapper_0.9.0.gemfile.lock +45 -0
  153. data/gemfiles/mongoid_2.0.0.gemfile +9 -0
  154. data/gemfiles/mongoid_2.0.0.gemfile.lock +49 -0
  155. data/gemfiles/mongoid_2.1.4.gemfile +9 -0
  156. data/gemfiles/mongoid_2.1.4.gemfile.lock +47 -0
  157. data/gemfiles/mongoid_2.2.4.gemfile +9 -0
  158. data/gemfiles/mongoid_2.2.4.gemfile.lock +47 -0
  159. data/gemfiles/mongoid_2.3.3.gemfile +9 -0
  160. data/gemfiles/mongoid_2.3.3.gemfile.lock +47 -0
  161. data/gemfiles/mongoid_2.4.0.gemfile +9 -0
  162. data/gemfiles/mongoid_2.4.0.gemfile.lock +47 -0
  163. data/gemfiles/mongoid_2.4.10.gemfile +9 -0
  164. data/gemfiles/mongoid_2.4.10.gemfile.lock +47 -0
  165. data/gemfiles/mongoid_2.5.2.gemfile +9 -0
  166. data/gemfiles/mongoid_2.5.2.gemfile.lock +47 -0
  167. data/gemfiles/mongoid_2.6.0.gemfile +9 -0
  168. data/gemfiles/mongoid_2.6.0.gemfile.lock +47 -0
  169. data/gemfiles/mongoid_3.0.0.gemfile +8 -0
  170. data/gemfiles/mongoid_3.0.0.gemfile.lock +45 -0
  171. data/gemfiles/mongoid_3.0.22.gemfile +8 -0
  172. data/gemfiles/mongoid_3.0.22.gemfile.lock +45 -0
  173. data/gemfiles/mongoid_3.1.0.gemfile +8 -0
  174. data/gemfiles/mongoid_3.1.0.gemfile.lock +45 -0
  175. data/gemfiles/sequel_2.11.0.gemfile +9 -0
  176. data/gemfiles/sequel_2.11.0.gemfile.lock +33 -0
  177. data/gemfiles/sequel_2.12.0.gemfile +9 -0
  178. data/gemfiles/sequel_2.12.0.gemfile.lock +33 -0
  179. data/gemfiles/sequel_2.8.0.gemfile +9 -0
  180. data/gemfiles/sequel_2.8.0.gemfile.lock +33 -0
  181. data/gemfiles/sequel_3.0.0.gemfile +9 -0
  182. data/gemfiles/sequel_3.0.0.gemfile.lock +33 -0
  183. data/gemfiles/sequel_3.10.0.gemfile +9 -0
  184. data/gemfiles/sequel_3.10.0.gemfile.lock +33 -0
  185. data/gemfiles/sequel_3.13.0.gemfile +9 -0
  186. data/gemfiles/sequel_3.13.0.gemfile.lock +33 -0
  187. data/gemfiles/sequel_3.14.0.gemfile +9 -0
  188. data/gemfiles/sequel_3.14.0.gemfile.lock +33 -0
  189. data/gemfiles/sequel_3.23.0.gemfile +9 -0
  190. data/gemfiles/sequel_3.23.0.gemfile.lock +33 -0
  191. data/gemfiles/sequel_3.24.0.gemfile +9 -0
  192. data/gemfiles/sequel_3.24.0.gemfile.lock +33 -0
  193. data/gemfiles/sequel_3.29.0.gemfile +9 -0
  194. data/gemfiles/sequel_3.29.0.gemfile.lock +33 -0
  195. data/gemfiles/sequel_3.34.0.gemfile +9 -0
  196. data/gemfiles/sequel_3.34.0.gemfile.lock +33 -0
  197. data/gemfiles/sequel_3.35.0.gemfile +9 -0
  198. data/gemfiles/sequel_3.35.0.gemfile.lock +33 -0
  199. data/gemfiles/sequel_3.4.0.gemfile +9 -0
  200. data/gemfiles/sequel_3.4.0.gemfile.lock +33 -0
  201. data/gemfiles/sequel_3.44.0.gemfile +9 -0
  202. data/gemfiles/sequel_3.44.0.gemfile.lock +33 -0
  203. data/init.rb +1 -0
  204. data/lib/state_machine.rb +8 -0
  205. data/lib/state_machine/assertions.rb +36 -0
  206. data/lib/state_machine/branch.rb +225 -0
  207. data/lib/state_machine/callback.rb +236 -0
  208. data/lib/state_machine/core.rb +12 -0
  209. data/lib/state_machine/core_ext.rb +2 -0
  210. data/lib/state_machine/core_ext/class/state_machine.rb +5 -0
  211. data/lib/state_machine/error.rb +13 -0
  212. data/lib/state_machine/eval_helpers.rb +87 -0
  213. data/lib/state_machine/event.rb +257 -0
  214. data/lib/state_machine/event_collection.rb +141 -0
  215. data/lib/state_machine/extensions.rb +149 -0
  216. data/lib/state_machine/graph.rb +92 -0
  217. data/lib/state_machine/helper_module.rb +17 -0
  218. data/lib/state_machine/initializers.rb +4 -0
  219. data/lib/state_machine/initializers/merb.rb +1 -0
  220. data/lib/state_machine/initializers/rails.rb +25 -0
  221. data/lib/state_machine/integrations.rb +121 -0
  222. data/lib/state_machine/integrations/active_model.rb +585 -0
  223. data/lib/state_machine/integrations/active_model/locale.rb +11 -0
  224. data/lib/state_machine/integrations/active_model/observer.rb +33 -0
  225. data/lib/state_machine/integrations/active_model/observer_update.rb +42 -0
  226. data/lib/state_machine/integrations/active_model/versions.rb +31 -0
  227. data/lib/state_machine/integrations/active_record.rb +552 -0
  228. data/lib/state_machine/integrations/active_record/locale.rb +20 -0
  229. data/lib/state_machine/integrations/active_record/versions.rb +123 -0
  230. data/lib/state_machine/integrations/base.rb +100 -0
  231. data/lib/state_machine/integrations/data_mapper.rb +511 -0
  232. data/lib/state_machine/integrations/data_mapper/observer.rb +210 -0
  233. data/lib/state_machine/integrations/data_mapper/versions.rb +85 -0
  234. data/lib/state_machine/integrations/mongo_mapper.rb +389 -0
  235. data/lib/state_machine/integrations/mongo_mapper/locale.rb +4 -0
  236. data/lib/state_machine/integrations/mongo_mapper/versions.rb +89 -0
  237. data/lib/state_machine/integrations/mongoid.rb +465 -0
  238. data/lib/state_machine/integrations/mongoid/locale.rb +4 -0
  239. data/lib/state_machine/integrations/mongoid/versions.rb +81 -0
  240. data/lib/state_machine/integrations/sequel.rb +486 -0
  241. data/lib/state_machine/integrations/sequel/versions.rb +95 -0
  242. data/lib/state_machine/machine.rb +2292 -0
  243. data/lib/state_machine/machine_collection.rb +86 -0
  244. data/lib/state_machine/macro_methods.rb +522 -0
  245. data/lib/state_machine/matcher.rb +123 -0
  246. data/lib/state_machine/matcher_helpers.rb +54 -0
  247. data/lib/state_machine/node_collection.rb +222 -0
  248. data/lib/state_machine/path.rb +120 -0
  249. data/lib/state_machine/path_collection.rb +90 -0
  250. data/lib/state_machine/state.rb +297 -0
  251. data/lib/state_machine/state_collection.rb +112 -0
  252. data/lib/state_machine/state_context.rb +138 -0
  253. data/lib/state_machine/transition.rb +470 -0
  254. data/lib/state_machine/transition_collection.rb +245 -0
  255. data/lib/state_machine/version.rb +3 -0
  256. data/lib/state_machine/yard.rb +8 -0
  257. data/lib/state_machine/yard/handlers.rb +12 -0
  258. data/lib/state_machine/yard/handlers/base.rb +32 -0
  259. data/lib/state_machine/yard/handlers/event.rb +25 -0
  260. data/lib/state_machine/yard/handlers/machine.rb +344 -0
  261. data/lib/state_machine/yard/handlers/state.rb +25 -0
  262. data/lib/state_machine/yard/handlers/transition.rb +47 -0
  263. data/lib/state_machine/yard/templates.rb +3 -0
  264. data/lib/state_machine/yard/templates/default/class/html/setup.rb +30 -0
  265. data/lib/state_machine/yard/templates/default/class/html/state_machines.erb +12 -0
  266. data/lib/tasks/state_machine.rake +1 -0
  267. data/lib/tasks/state_machine.rb +30 -0
  268. data/lib/yard-state_machine.rb +2 -0
  269. data/state_machine.gemspec +22 -0
  270. data/test/files/en.yml +17 -0
  271. data/test/files/switch.rb +15 -0
  272. data/test/functional/state_machine_test.rb +1066 -0
  273. data/test/test_helper.rb +7 -0
  274. data/test/unit/assertions_test.rb +40 -0
  275. data/test/unit/branch_test.rb +969 -0
  276. data/test/unit/callback_test.rb +704 -0
  277. data/test/unit/error_test.rb +43 -0
  278. data/test/unit/eval_helpers_test.rb +270 -0
  279. data/test/unit/event_collection_test.rb +398 -0
  280. data/test/unit/event_test.rb +1196 -0
  281. data/test/unit/graph_test.rb +98 -0
  282. data/test/unit/helper_module_test.rb +17 -0
  283. data/test/unit/integrations/active_model_test.rb +1245 -0
  284. data/test/unit/integrations/active_record_test.rb +2551 -0
  285. data/test/unit/integrations/base_test.rb +104 -0
  286. data/test/unit/integrations/data_mapper_test.rb +2194 -0
  287. data/test/unit/integrations/mongo_mapper_test.rb +2026 -0
  288. data/test/unit/integrations/mongoid_test.rb +2309 -0
  289. data/test/unit/integrations/sequel_test.rb +1896 -0
  290. data/test/unit/integrations_test.rb +83 -0
  291. data/test/unit/invalid_event_test.rb +20 -0
  292. data/test/unit/invalid_parallel_transition_test.rb +18 -0
  293. data/test/unit/invalid_transition_test.rb +115 -0
  294. data/test/unit/machine_collection_test.rb +603 -0
  295. data/test/unit/machine_test.rb +3431 -0
  296. data/test/unit/matcher_helpers_test.rb +37 -0
  297. data/test/unit/matcher_test.rb +155 -0
  298. data/test/unit/node_collection_test.rb +362 -0
  299. data/test/unit/path_collection_test.rb +266 -0
  300. data/test/unit/path_test.rb +485 -0
  301. data/test/unit/state_collection_test.rb +352 -0
  302. data/test/unit/state_context_test.rb +441 -0
  303. data/test/unit/state_machine_test.rb +31 -0
  304. data/test/unit/state_test.rb +1101 -0
  305. data/test/unit/transition_collection_test.rb +2168 -0
  306. data/test/unit/transition_test.rb +1558 -0
  307. metadata +435 -0
@@ -0,0 +1,3431 @@
1
+ require File.expand_path(File.dirname(__FILE__) + '/../test_helper')
2
+
3
+ class MachineByDefaultTest < Test::Unit::TestCase
4
+ def setup
5
+ @klass = Class.new
6
+ @machine = StateMachine::Machine.new(@klass)
7
+ @object = @klass.new
8
+ end
9
+
10
+ def test_should_have_an_owner_class
11
+ assert_equal @klass, @machine.owner_class
12
+ end
13
+
14
+ def test_should_have_a_name
15
+ assert_equal :state, @machine.name
16
+ end
17
+
18
+ def test_should_have_an_attribute
19
+ assert_equal :state, @machine.attribute
20
+ end
21
+
22
+ def test_should_prefix_custom_attributes_with_attribute
23
+ assert_equal :state_event, @machine.attribute(:event)
24
+ end
25
+
26
+ def test_should_have_an_initial_state
27
+ assert_not_nil @machine.initial_state(@object)
28
+ end
29
+
30
+ def test_should_have_a_nil_initial_state
31
+ assert_nil @machine.initial_state(@object).value
32
+ end
33
+
34
+ def test_should_not_have_any_events
35
+ assert !@machine.events.any?
36
+ end
37
+
38
+ def test_should_not_have_any_before_callbacks
39
+ assert @machine.callbacks[:before].empty?
40
+ end
41
+
42
+ def test_should_not_have_any_after_callbacks
43
+ assert @machine.callbacks[:after].empty?
44
+ end
45
+
46
+ def test_should_not_have_any_failure_callbacks
47
+ assert @machine.callbacks[:failure].empty?
48
+ end
49
+
50
+ def test_should_not_have_an_action
51
+ assert_nil @machine.action
52
+ end
53
+
54
+ def test_should_use_tranactions
55
+ assert_equal true, @machine.use_transactions
56
+ end
57
+
58
+ def test_should_not_have_a_namespace
59
+ assert_nil @machine.namespace
60
+ end
61
+
62
+ def test_should_have_a_nil_state
63
+ assert_equal [nil], @machine.states.keys
64
+ end
65
+
66
+ def test_should_set_initial_on_nil_state
67
+ assert @machine.state(nil).initial
68
+ end
69
+
70
+ def test_should_generate_default_messages
71
+ assert_equal 'is invalid', @machine.generate_message(:invalid)
72
+ assert_equal 'cannot transition when parked', @machine.generate_message(:invalid_event, [[:state, :parked]])
73
+ assert_equal 'cannot transition via "park"', @machine.generate_message(:invalid_transition, [[:event, :park]])
74
+ end
75
+
76
+ def test_should_not_be_extended_by_the_base_integration
77
+ assert !(class << @machine; ancestors; end).include?(StateMachine::Integrations::Base)
78
+ end
79
+
80
+ def test_should_not_be_extended_by_the_active_model_integration
81
+ assert !(class << @machine; ancestors; end).include?(StateMachine::Integrations::ActiveModel)
82
+ end
83
+
84
+ def test_should_not_be_extended_by_the_active_record_integration
85
+ assert !(class << @machine; ancestors; end).include?(StateMachine::Integrations::ActiveRecord)
86
+ end
87
+
88
+ def test_should_not_be_extended_by_the_datamapper_integration
89
+ assert !(class << @machine; ancestors; end).include?(StateMachine::Integrations::DataMapper)
90
+ end
91
+
92
+ def test_should_not_be_extended_by_the_mongo_mapper_integration
93
+ assert !(class << @machine; ancestors; end).include?(StateMachine::Integrations::MongoMapper)
94
+ end
95
+
96
+ def test_should_not_be_extended_by_the_sequel_integration
97
+ assert !(class << @machine; ancestors; end).include?(StateMachine::Integrations::Sequel)
98
+ end
99
+
100
+ def test_should_define_a_reader_attribute_for_the_attribute
101
+ assert @object.respond_to?(:state)
102
+ end
103
+
104
+ def test_should_define_a_writer_attribute_for_the_attribute
105
+ assert @object.respond_to?(:state=)
106
+ end
107
+
108
+ def test_should_define_a_predicate_for_the_attribute
109
+ assert @object.respond_to?(:state?)
110
+ end
111
+
112
+ def test_should_define_a_name_reader_for_the_attribute
113
+ assert @object.respond_to?(:state_name)
114
+ end
115
+
116
+ def test_should_define_an_event_reader_for_the_attribute
117
+ assert @object.respond_to?(:state_events)
118
+ end
119
+
120
+ def test_should_define_a_transition_reader_for_the_attribute
121
+ assert @object.respond_to?(:state_transitions)
122
+ end
123
+
124
+ def test_should_define_a_path_reader_for_the_attribute
125
+ assert @object.respond_to?(:state_paths)
126
+ end
127
+
128
+ def test_should_define_an_event_runner_for_the_attribute
129
+ assert @object.respond_to?(:fire_state_event)
130
+ end
131
+
132
+ def test_should_not_define_an_event_attribute_reader
133
+ assert !@object.respond_to?(:state_event)
134
+ end
135
+
136
+ def test_should_not_define_an_event_attribute_writer
137
+ assert !@object.respond_to?(:state_event=)
138
+ end
139
+
140
+ def test_should_not_define_an_event_transition_attribute_reader
141
+ assert !@object.respond_to?(:state_event_transition)
142
+ end
143
+
144
+ def test_should_not_define_an_event_transition_attribute_writer
145
+ assert !@object.respond_to?(:state_event_transition=)
146
+ end
147
+
148
+ def test_should_define_a_human_attribute_name_reader_for_the_attribute
149
+ assert @klass.respond_to?(:human_state_name)
150
+ end
151
+
152
+ def test_should_define_a_human_event_name_reader_for_the_attribute
153
+ assert @klass.respond_to?(:human_state_event_name)
154
+ end
155
+
156
+ def test_should_not_define_singular_with_scope
157
+ assert !@klass.respond_to?(:with_state)
158
+ end
159
+
160
+ def test_should_not_define_singular_without_scope
161
+ assert !@klass.respond_to?(:without_state)
162
+ end
163
+
164
+ def test_should_not_define_plural_with_scope
165
+ assert !@klass.respond_to?(:with_states)
166
+ end
167
+
168
+ def test_should_not_define_plural_without_scope
169
+ assert !@klass.respond_to?(:without_states)
170
+ end
171
+
172
+ def test_should_extend_owner_class_with_class_methods
173
+ assert((class << @klass; ancestors; end).include?(StateMachine::ClassMethods))
174
+ end
175
+
176
+ def test_should_include_instance_methods_in_owner_class
177
+ assert @klass.included_modules.include?(StateMachine::InstanceMethods)
178
+ end
179
+
180
+ def test_should_define_state_machines_reader
181
+ expected = {:state => @machine}
182
+ assert_equal expected, @klass.state_machines
183
+ end
184
+ end
185
+
186
+ class MachineWithCustomNameTest < Test::Unit::TestCase
187
+ def setup
188
+ @klass = Class.new
189
+ @machine = StateMachine::Machine.new(@klass, :status)
190
+ @object = @klass.new
191
+ end
192
+
193
+ def test_should_use_custom_name
194
+ assert_equal :status, @machine.name
195
+ end
196
+
197
+ def test_should_use_custom_name_for_attribute
198
+ assert_equal :status, @machine.attribute
199
+ end
200
+
201
+ def test_should_prefix_custom_attributes_with_custom_name
202
+ assert_equal :status_event, @machine.attribute(:event)
203
+ end
204
+
205
+ def test_should_define_a_reader_attribute_for_the_attribute
206
+ assert @object.respond_to?(:status)
207
+ end
208
+
209
+ def test_should_define_a_writer_attribute_for_the_attribute
210
+ assert @object.respond_to?(:status=)
211
+ end
212
+
213
+ def test_should_define_a_predicate_for_the_attribute
214
+ assert @object.respond_to?(:status?)
215
+ end
216
+
217
+ def test_should_define_a_name_reader_for_the_attribute
218
+ assert @object.respond_to?(:status_name)
219
+ end
220
+
221
+ def test_should_define_an_event_reader_for_the_attribute
222
+ assert @object.respond_to?(:status_events)
223
+ end
224
+
225
+ def test_should_define_a_transition_reader_for_the_attribute
226
+ assert @object.respond_to?(:status_transitions)
227
+ end
228
+
229
+ def test_should_define_an_event_runner_for_the_attribute
230
+ assert @object.respond_to?(:fire_status_event)
231
+ end
232
+
233
+ def test_should_define_a_human_attribute_name_reader_for_the_attribute
234
+ assert @klass.respond_to?(:human_status_name)
235
+ end
236
+
237
+ def test_should_define_a_human_event_name_reader_for_the_attribute
238
+ assert @klass.respond_to?(:human_status_event_name)
239
+ end
240
+ end
241
+
242
+ class MachineWithoutInitializationTest < Test::Unit::TestCase
243
+ def setup
244
+ @klass = Class.new do
245
+ def initialize(attributes = {})
246
+ attributes.each {|attr, value| send("#{attr}=", value)}
247
+ super()
248
+ end
249
+ end
250
+
251
+ @machine = StateMachine::Machine.new(@klass, :initial => :parked, :initialize => false)
252
+ end
253
+
254
+ def test_should_not_have_an_initial_state
255
+ object = @klass.new
256
+ assert_nil object.state
257
+ end
258
+
259
+ def test_should_still_allow_manual_initialization
260
+ @klass.send(:include, Module.new do
261
+ def initialize(attributes = {})
262
+ super()
263
+ initialize_state_machines
264
+ end
265
+ end)
266
+
267
+ object = @klass.new
268
+ assert_equal 'parked', object.state
269
+ end
270
+ end
271
+
272
+ class MachineWithStaticInitialStateTest < Test::Unit::TestCase
273
+ def setup
274
+ @klass = Class.new
275
+ @machine = StateMachine::Machine.new(@klass, :initial => :parked)
276
+ end
277
+
278
+ def test_should_not_have_dynamic_initial_state
279
+ assert !@machine.dynamic_initial_state?
280
+ end
281
+
282
+ def test_should_have_an_initial_state
283
+ object = @klass.new
284
+ assert_equal 'parked', @machine.initial_state(object).value
285
+ end
286
+
287
+ def test_should_write_to_attribute_when_initializing_state
288
+ object = @klass.allocate
289
+ @machine.initialize_state(object)
290
+ assert_equal 'parked', object.state
291
+ end
292
+
293
+ def test_should_set_initial_on_state_object
294
+ assert @machine.state(:parked).initial
295
+ end
296
+
297
+ def test_should_set_initial_state_on_created_object
298
+ assert_equal 'parked', @klass.new.state
299
+ end
300
+
301
+ def test_not_set_initial_state_even_if_not_empty
302
+ @klass.class_eval do
303
+ def initialize(attributes = {})
304
+ self.state = 'idling'
305
+ super()
306
+ end
307
+ end
308
+ object = @klass.new
309
+ assert_equal 'idling', object.state
310
+ end
311
+
312
+ def test_should_set_initial_state_prior_to_initialization
313
+ base = Class.new do
314
+ attr_accessor :state_on_init
315
+
316
+ def initialize
317
+ self.state_on_init = state
318
+ end
319
+ end
320
+ klass = Class.new(base)
321
+ StateMachine::Machine.new(klass, :initial => :parked)
322
+
323
+ assert_equal 'parked', klass.new.state_on_init
324
+ end
325
+
326
+ def test_should_be_included_in_known_states
327
+ assert_equal [:parked], @machine.states.keys
328
+ end
329
+ end
330
+
331
+ class MachineWithInitialStateWithValueAndOwnerDefault < Test::Unit::TestCase
332
+ def setup
333
+ @original_stderr, $stderr = $stderr, StringIO.new
334
+
335
+ state_machine_with_defaults = Class.new(StateMachine::Machine) do
336
+ def owner_class_attribute_default
337
+ 0
338
+ end
339
+ end
340
+ @klass = Class.new
341
+ @machine = state_machine_with_defaults.new(@klass, :initial => :parked) do
342
+ state :parked, :value => 0
343
+ end
344
+ end
345
+
346
+ def test_should_not_warn_about_wrong_default
347
+ assert_equal '', $stderr.string
348
+ end
349
+
350
+ def teardown
351
+ $stderr = @original_stderr
352
+ end
353
+ end
354
+
355
+ class MachineWithDynamicInitialStateTest < Test::Unit::TestCase
356
+ def setup
357
+ @klass = Class.new do
358
+ attr_accessor :initial_state
359
+ end
360
+ @machine = StateMachine::Machine.new(@klass, :initial => lambda {|object| object.initial_state || :default})
361
+ @machine.state :parked, :idling, :default
362
+ @object = @klass.new
363
+ end
364
+
365
+ def test_should_have_dynamic_initial_state
366
+ assert @machine.dynamic_initial_state?
367
+ end
368
+
369
+ def test_should_use_the_record_for_determining_the_initial_state
370
+ @object.initial_state = :parked
371
+ assert_equal :parked, @machine.initial_state(@object).name
372
+
373
+ @object.initial_state = :idling
374
+ assert_equal :idling, @machine.initial_state(@object).name
375
+ end
376
+
377
+ def test_should_write_to_attribute_when_initializing_state
378
+ object = @klass.allocate
379
+ object.initial_state = :parked
380
+ @machine.initialize_state(object)
381
+ assert_equal 'parked', object.state
382
+ end
383
+
384
+ def test_should_set_initial_state_on_created_object
385
+ assert_equal 'default', @object.state
386
+ end
387
+
388
+ def test_should_not_set_initial_state_even_if_not_empty
389
+ @klass.class_eval do
390
+ def initialize(attributes = {})
391
+ self.state = 'parked'
392
+ super()
393
+ end
394
+ end
395
+ object = @klass.new
396
+ assert_equal 'parked', object.state
397
+ end
398
+
399
+ def test_should_set_initial_state_after_initialization
400
+ base = Class.new do
401
+ attr_accessor :state_on_init
402
+
403
+ def initialize
404
+ self.state_on_init = state
405
+ end
406
+ end
407
+ klass = Class.new(base)
408
+ machine = StateMachine::Machine.new(klass, :initial => lambda {|object| :parked})
409
+ machine.state :parked
410
+
411
+ assert_nil klass.new.state_on_init
412
+ end
413
+
414
+ def test_should_not_be_included_in_known_states
415
+ assert_equal [:parked, :idling, :default], @machine.states.map {|state| state.name}
416
+ end
417
+ end
418
+
419
+ class MachineStateInitializationTest < Test::Unit::TestCase
420
+ def setup
421
+ @klass = Class.new
422
+ @machine = StateMachine::Machine.new(@klass, :state, :initial => :parked, :initialize => false)
423
+
424
+ @object = @klass.new
425
+ @object.state = nil
426
+ end
427
+
428
+ def test_should_set_states_if_nil
429
+ @machine.initialize_state(@object)
430
+
431
+ assert_equal 'parked', @object.state
432
+ end
433
+
434
+ def test_should_set_states_if_empty
435
+ @object.state = ''
436
+ @machine.initialize_state(@object)
437
+
438
+ assert_equal 'parked', @object.state
439
+ end
440
+
441
+ def test_should_not_set_states_if_not_empty
442
+ @object.state = 'idling'
443
+ @machine.initialize_state(@object)
444
+
445
+ assert_equal 'idling', @object.state
446
+ end
447
+
448
+ def test_should_set_states_if_not_empty_and_forced
449
+ @object.state = 'idling'
450
+ @machine.initialize_state(@object, :force => true)
451
+
452
+ assert_equal 'parked', @object.state
453
+ end
454
+
455
+ def test_should_not_set_state_if_nil_and_nil_is_valid_state
456
+ @machine.state :initial, :value => nil
457
+ @machine.initialize_state(@object)
458
+
459
+ assert_nil @object.state
460
+ end
461
+
462
+ def test_should_write_to_hash_if_specified
463
+ @machine.initialize_state(@object, :to => hash = {})
464
+ assert_equal({'state' => 'parked'}, hash)
465
+ end
466
+
467
+ def test_should_not_write_to_object_if_writing_to_hash
468
+ @machine.initialize_state(@object, :to => {})
469
+ assert_nil @object.state
470
+ end
471
+ end
472
+
473
+ class MachineWithCustomActionTest < Test::Unit::TestCase
474
+ def setup
475
+ @machine = StateMachine::Machine.new(Class.new, :action => :save)
476
+ end
477
+
478
+ def test_should_use_the_custom_action
479
+ assert_equal :save, @machine.action
480
+ end
481
+ end
482
+
483
+ class MachineWithNilActionTest < Test::Unit::TestCase
484
+ def setup
485
+ integration = Module.new do
486
+ include StateMachine::Integrations::Base
487
+
488
+ @defaults = {:action => :save}
489
+ end
490
+ StateMachine::Integrations.const_set('Custom', integration)
491
+ @machine = StateMachine::Machine.new(Class.new, :action => nil, :integration => :custom)
492
+ end
493
+
494
+ def test_should_have_a_nil_action
495
+ assert_nil @machine.action
496
+ end
497
+
498
+ def teardown
499
+ StateMachine::Integrations.send(:remove_const, 'Custom')
500
+ end
501
+ end
502
+
503
+ class MachineWithoutIntegrationTest < Test::Unit::TestCase
504
+ def setup
505
+ @klass = Class.new
506
+ @machine = StateMachine::Machine.new(@klass)
507
+ @object = @klass.new
508
+ end
509
+
510
+ def test_transaction_should_yield
511
+ @yielded = false
512
+ @machine.within_transaction(@object) do
513
+ @yielded = true
514
+ end
515
+
516
+ assert @yielded
517
+ end
518
+
519
+ def test_invalidation_should_do_nothing
520
+ assert_nil @machine.invalidate(@object, :state, :invalid_transition, [[:event, 'park']])
521
+ end
522
+
523
+ def test_reset_should_do_nothing
524
+ assert_nil @machine.reset(@object)
525
+ end
526
+
527
+ def test_errors_for_should_be_empty
528
+ assert_equal '', @machine.errors_for(@object)
529
+ end
530
+ end
531
+
532
+ class MachineWithCustomIntegrationTest < Test::Unit::TestCase
533
+ def setup
534
+ integration = Module.new do
535
+ include StateMachine::Integrations::Base
536
+
537
+ def self.matching_ancestors
538
+ ['MachineWithCustomIntegrationTest::Vehicle']
539
+ end
540
+ end
541
+
542
+ StateMachine::Integrations.const_set('Custom', integration)
543
+
544
+ superclass = Class.new
545
+ self.class.const_set('Vehicle', superclass)
546
+
547
+ @klass = Class.new(superclass)
548
+ end
549
+
550
+ def test_should_be_extended_by_the_integration_if_explicit
551
+ machine = StateMachine::Machine.new(@klass, :integration => :custom)
552
+ assert((class << machine; ancestors; end).include?(StateMachine::Integrations::Custom))
553
+ end
554
+
555
+ def test_should_not_be_extended_by_the_integration_if_implicit_but_not_available
556
+ StateMachine::Integrations::Custom.class_eval do
557
+ class << self; remove_method :matching_ancestors; end
558
+ def self.matching_ancestors
559
+ []
560
+ end
561
+ end
562
+
563
+ machine = StateMachine::Machine.new(@klass)
564
+ assert(!(class << machine; ancestors; end).include?(StateMachine::Integrations::Custom))
565
+ end
566
+
567
+ def test_should_not_be_extended_by_the_integration_if_implicit_but_not_matched
568
+ StateMachine::Integrations::Custom.class_eval do
569
+ class << self; remove_method :matching_ancestors; end
570
+ def self.matching_ancestors
571
+ []
572
+ end
573
+ end
574
+
575
+ machine = StateMachine::Machine.new(@klass)
576
+ assert(!(class << machine; ancestors; end).include?(StateMachine::Integrations::Custom))
577
+ end
578
+
579
+ def test_should_be_extended_by_the_integration_if_implicit_and_available_and_matches
580
+ machine = StateMachine::Machine.new(@klass)
581
+ assert((class << machine; ancestors; end).include?(StateMachine::Integrations::Custom))
582
+ end
583
+
584
+ def test_should_not_be_extended_by_the_integration_if_nil
585
+ machine = StateMachine::Machine.new(@klass, :integration => nil)
586
+ assert(!(class << machine; ancestors; end).include?(StateMachine::Integrations::Custom))
587
+ end
588
+
589
+ def test_should_not_be_extended_by_the_integration_if_false
590
+ machine = StateMachine::Machine.new(@klass, :integration => false)
591
+ assert(!(class << machine; ancestors; end).include?(StateMachine::Integrations::Custom))
592
+ end
593
+
594
+ def teardown
595
+ self.class.send(:remove_const, 'Vehicle')
596
+ StateMachine::Integrations.send(:remove_const, 'Custom')
597
+ end
598
+ end
599
+
600
+ class MachineWithIntegrationTest < Test::Unit::TestCase
601
+ def setup
602
+ StateMachine::Integrations.const_set('Custom', Module.new do
603
+ include StateMachine::Integrations::Base
604
+
605
+ @defaults = {:action => :save, :use_transactions => false}
606
+
607
+ attr_reader :initialized, :with_scopes, :without_scopes, :ran_transaction
608
+
609
+ def after_initialize
610
+ @initialized = true
611
+ end
612
+
613
+ def create_with_scope(name)
614
+ (@with_scopes ||= []) << name
615
+ lambda {}
616
+ end
617
+
618
+ def create_without_scope(name)
619
+ (@without_scopes ||= []) << name
620
+ lambda {}
621
+ end
622
+
623
+ def transaction(object)
624
+ @ran_transaction = true
625
+ yield
626
+ end
627
+ end)
628
+
629
+ @machine = StateMachine::Machine.new(Class.new, :integration => :custom)
630
+ end
631
+
632
+ def test_should_call_after_initialize_hook
633
+ assert @machine.initialized
634
+ end
635
+
636
+ def test_should_use_the_default_action
637
+ assert_equal :save, @machine.action
638
+ end
639
+
640
+ def test_should_use_the_custom_action_if_specified
641
+ machine = StateMachine::Machine.new(Class.new, :integration => :custom, :action => :save!)
642
+ assert_equal :save!, machine.action
643
+ end
644
+
645
+ def test_should_use_the_default_use_transactions
646
+ assert_equal false, @machine.use_transactions
647
+ end
648
+
649
+ def test_should_use_the_custom_use_transactions_if_specified
650
+ machine = StateMachine::Machine.new(Class.new, :integration => :custom, :use_transactions => true)
651
+ assert_equal true, machine.use_transactions
652
+ end
653
+
654
+ def test_should_define_a_singular_and_plural_with_scope
655
+ assert_equal %w(with_state with_states), @machine.with_scopes
656
+ end
657
+
658
+ def test_should_define_a_singular_and_plural_without_scope
659
+ assert_equal %w(without_state without_states), @machine.without_scopes
660
+ end
661
+
662
+ def teardown
663
+ StateMachine::Integrations.send(:remove_const, 'Custom')
664
+ end
665
+ end
666
+
667
+ class MachineWithActionUndefinedTest < Test::Unit::TestCase
668
+ def setup
669
+ @klass = Class.new
670
+ @machine = StateMachine::Machine.new(@klass, :action => :save)
671
+ @object = @klass.new
672
+ end
673
+
674
+ def test_should_define_an_event_attribute_reader
675
+ assert @object.respond_to?(:state_event)
676
+ end
677
+
678
+ def test_should_define_an_event_attribute_writer
679
+ assert @object.respond_to?(:state_event=)
680
+ end
681
+
682
+ def test_should_define_an_event_transition_attribute_reader
683
+ assert @object.respond_to?(:state_event_transition, true)
684
+ end
685
+
686
+ def test_should_define_an_event_transition_attribute_writer
687
+ assert @object.respond_to?(:state_event_transition=, true)
688
+ end
689
+
690
+ def test_should_not_define_action
691
+ assert !@object.respond_to?(:save)
692
+ end
693
+
694
+ def test_should_not_mark_action_hook_as_defined
695
+ assert !@machine.action_hook?
696
+ end
697
+ end
698
+
699
+ class MachineWithActionDefinedInClassTest < Test::Unit::TestCase
700
+ def setup
701
+ @klass = Class.new do
702
+ def save
703
+ end
704
+ end
705
+
706
+ @machine = StateMachine::Machine.new(@klass, :action => :save)
707
+ @object = @klass.new
708
+ end
709
+
710
+ def test_should_define_an_event_attribute_reader
711
+ assert @object.respond_to?(:state_event)
712
+ end
713
+
714
+ def test_should_define_an_event_attribute_writer
715
+ assert @object.respond_to?(:state_event=)
716
+ end
717
+
718
+ def test_should_define_an_event_transition_attribute_reader
719
+ assert @object.respond_to?(:state_event_transition, true)
720
+ end
721
+
722
+ def test_should_define_an_event_transition_attribute_writer
723
+ assert @object.respond_to?(:state_event_transition=, true)
724
+ end
725
+
726
+ def test_should_not_define_action
727
+ assert !@klass.ancestors.any? {|ancestor| ancestor != @klass && ancestor.method_defined?(:save)}
728
+ end
729
+
730
+ def test_should_not_mark_action_hook_as_defined
731
+ assert !@machine.action_hook?
732
+ end
733
+ end
734
+
735
+ class MachineWithActionDefinedInIncludedModuleTest < Test::Unit::TestCase
736
+ def setup
737
+ @mod = mod = Module.new do
738
+ def save
739
+ end
740
+ end
741
+
742
+ @klass = Class.new do
743
+ include mod
744
+ end
745
+
746
+ @machine = StateMachine::Machine.new(@klass, :action => :save)
747
+ @object = @klass.new
748
+ end
749
+
750
+ def test_should_define_an_event_attribute_reader
751
+ assert @object.respond_to?(:state_event)
752
+ end
753
+
754
+ def test_should_define_an_event_attribute_writer
755
+ assert @object.respond_to?(:state_event=)
756
+ end
757
+
758
+ def test_should_define_an_event_transition_attribute_reader
759
+ assert @object.respond_to?(:state_event_transition, true)
760
+ end
761
+
762
+ def test_should_define_an_event_transition_attribute_writer
763
+ assert @object.respond_to?(:state_event_transition=, true)
764
+ end
765
+
766
+ def test_should_define_action
767
+ assert @klass.ancestors.any? {|ancestor| ![@klass, @mod].include?(ancestor) && ancestor.method_defined?(:save)}
768
+ end
769
+
770
+ def test_should_keep_action_public
771
+ assert @klass.public_method_defined?(:save)
772
+ end
773
+
774
+ def test_should_mark_action_hook_as_defined
775
+ assert @machine.action_hook?
776
+ end
777
+ end
778
+
779
+ class MachineWithActionDefinedInSuperclassTest < Test::Unit::TestCase
780
+ def setup
781
+ @superclass = Class.new do
782
+ def save
783
+ end
784
+ end
785
+ @klass = Class.new(@superclass)
786
+
787
+ @machine = StateMachine::Machine.new(@klass, :action => :save)
788
+ @object = @klass.new
789
+ end
790
+
791
+ def test_should_define_an_event_attribute_reader
792
+ assert @object.respond_to?(:state_event)
793
+ end
794
+
795
+ def test_should_define_an_event_attribute_writer
796
+ assert @object.respond_to?(:state_event=)
797
+ end
798
+
799
+ def test_should_define_an_event_transition_attribute_reader
800
+ assert @object.respond_to?(:state_event_transition, true)
801
+ end
802
+
803
+ def test_should_define_an_event_transition_attribute_writer
804
+ assert @object.respond_to?(:state_event_transition=, true)
805
+ end
806
+
807
+ def test_should_define_action
808
+ assert @klass.ancestors.any? {|ancestor| ![@klass, @superclass].include?(ancestor) && ancestor.method_defined?(:save)}
809
+ end
810
+
811
+ def test_should_keep_action_public
812
+ assert @klass.public_method_defined?(:save)
813
+ end
814
+
815
+ def test_should_mark_action_hook_as_defined
816
+ assert @machine.action_hook?
817
+ end
818
+ end
819
+
820
+ class MachineWithPrivateActionTest < Test::Unit::TestCase
821
+ def setup
822
+ @superclass = Class.new do
823
+ private
824
+ def save
825
+ end
826
+ end
827
+ @klass = Class.new(@superclass)
828
+
829
+ @machine = StateMachine::Machine.new(@klass, :action => :save)
830
+ @object = @klass.new
831
+ end
832
+
833
+ def test_should_define_an_event_attribute_reader
834
+ assert @object.respond_to?(:state_event)
835
+ end
836
+
837
+ def test_should_define_an_event_attribute_writer
838
+ assert @object.respond_to?(:state_event=)
839
+ end
840
+
841
+ def test_should_define_an_event_transition_attribute_reader
842
+ assert @object.respond_to?(:state_event_transition, true)
843
+ end
844
+
845
+ def test_should_define_an_event_transition_attribute_writer
846
+ assert @object.respond_to?(:state_event_transition=, true)
847
+ end
848
+
849
+ def test_should_define_action
850
+ assert @klass.ancestors.any? {|ancestor| ![@klass, @superclass].include?(ancestor) && ancestor.private_method_defined?(:save)}
851
+ end
852
+
853
+ def test_should_keep_action_private
854
+ assert @klass.private_method_defined?(:save)
855
+ end
856
+
857
+ def test_should_mark_action_hook_as_defined
858
+ assert @machine.action_hook?
859
+ end
860
+ end
861
+
862
+ class MachineWithActionAlreadyOverriddenTest < Test::Unit::TestCase
863
+ def setup
864
+ @superclass = Class.new do
865
+ def save
866
+ end
867
+ end
868
+ @klass = Class.new(@superclass)
869
+
870
+ StateMachine::Machine.new(@klass, :action => :save)
871
+ @machine = StateMachine::Machine.new(@klass, :status, :action => :save)
872
+ @object = @klass.new
873
+ end
874
+
875
+ def test_should_not_redefine_action
876
+ assert_equal 1, @klass.ancestors.select {|ancestor| ![@klass, @superclass].include?(ancestor) && ancestor.method_defined?(:save)}.length
877
+ end
878
+
879
+ def test_should_mark_action_hook_as_defined
880
+ assert @machine.action_hook?
881
+ end
882
+ end
883
+
884
+ class MachineWithCustomPluralTest < Test::Unit::TestCase
885
+ def setup
886
+ @integration = Module.new do
887
+ include StateMachine::Integrations::Base
888
+
889
+ class << self; attr_accessor :with_scopes, :without_scopes; end
890
+ @with_scopes = []
891
+ @without_scopes = []
892
+
893
+ def create_with_scope(name)
894
+ StateMachine::Integrations::Custom.with_scopes << name
895
+ lambda {}
896
+ end
897
+
898
+ def create_without_scope(name)
899
+ StateMachine::Integrations::Custom.without_scopes << name
900
+ lambda {}
901
+ end
902
+ end
903
+
904
+ StateMachine::Integrations.const_set('Custom', @integration)
905
+ end
906
+
907
+ def test_should_define_a_singular_and_plural_with_scope
908
+ StateMachine::Machine.new(Class.new, :integration => :custom, :plural => 'staties')
909
+ assert_equal %w(with_state with_staties), @integration.with_scopes
910
+ end
911
+
912
+ def test_should_define_a_singular_and_plural_without_scope
913
+ StateMachine::Machine.new(Class.new, :integration => :custom, :plural => 'staties')
914
+ assert_equal %w(without_state without_staties), @integration.without_scopes
915
+ end
916
+
917
+ def test_should_define_single_with_scope_if_singular_same_as_plural
918
+ StateMachine::Machine.new(Class.new, :integration => :custom, :plural => 'state')
919
+ assert_equal %w(with_state), @integration.with_scopes
920
+ end
921
+
922
+ def test_should_define_single_without_scope_if_singular_same_as_plural
923
+ StateMachine::Machine.new(Class.new, :integration => :custom, :plural => 'state')
924
+ assert_equal %w(without_state), @integration.without_scopes
925
+ end
926
+
927
+ def teardown
928
+ StateMachine::Integrations.send(:remove_const, 'Custom')
929
+ end
930
+ end
931
+
932
+ class MachineWithCustomInvalidationTest < Test::Unit::TestCase
933
+ def setup
934
+ @integration = Module.new do
935
+ include StateMachine::Integrations::Base
936
+
937
+ def invalidate(object, attribute, message, values = [])
938
+ object.error = generate_message(message, values)
939
+ end
940
+ end
941
+ StateMachine::Integrations.const_set('Custom', @integration)
942
+
943
+ @klass = Class.new do
944
+ attr_accessor :error
945
+ end
946
+
947
+ @machine = StateMachine::Machine.new(@klass, :integration => :custom, :messages => {:invalid_transition => 'cannot %s'})
948
+ @machine.state :parked
949
+
950
+ @object = @klass.new
951
+ @object.state = 'parked'
952
+ end
953
+
954
+ def test_generate_custom_message
955
+ assert_equal 'cannot park', @machine.generate_message(:invalid_transition, [[:event, :park]])
956
+ end
957
+
958
+ def test_use_custom_message
959
+ @machine.invalidate(@object, :state, :invalid_transition, [[:event, 'park']])
960
+ assert_equal 'cannot park', @object.error
961
+ end
962
+
963
+ def teardown
964
+ StateMachine::Integrations.send(:remove_const, 'Custom')
965
+ end
966
+ end
967
+
968
+ class MachineTest < Test::Unit::TestCase
969
+ def test_should_raise_exception_if_invalid_option_specified
970
+ assert_raise(ArgumentError) {StateMachine::Machine.new(Class.new, :invalid => true)}
971
+ end
972
+
973
+ def test_should_not_raise_exception_if_custom_messages_specified
974
+ assert_nothing_raised {StateMachine::Machine.new(Class.new, :messages => {:invalid_transition => 'custom'})}
975
+ end
976
+
977
+ def test_should_evaluate_a_block_during_initialization
978
+ called = true
979
+ StateMachine::Machine.new(Class.new) do
980
+ called = respond_to?(:event)
981
+ end
982
+
983
+ assert called
984
+ end
985
+
986
+ def test_should_provide_matcher_helpers_during_initialization
987
+ matchers = []
988
+
989
+ StateMachine::Machine.new(Class.new) do
990
+ matchers = [all, any, same]
991
+ end
992
+
993
+ assert_equal [StateMachine::AllMatcher.instance, StateMachine::AllMatcher.instance, StateMachine::LoopbackMatcher.instance], matchers
994
+ end
995
+ end
996
+
997
+ class MachineAfterBeingCopiedTest < Test::Unit::TestCase
998
+ def setup
999
+ @machine = StateMachine::Machine.new(Class.new, :state, :initial => :parked)
1000
+ @machine.event(:ignite) {}
1001
+ @machine.before_transition(lambda {})
1002
+ @machine.after_transition(lambda {})
1003
+ @machine.around_transition(lambda {})
1004
+ @machine.after_failure(lambda {})
1005
+
1006
+ @copied_machine = @machine.clone
1007
+ end
1008
+
1009
+ def test_should_not_have_the_same_collection_of_states
1010
+ assert_not_same @copied_machine.states, @machine.states
1011
+ end
1012
+
1013
+ def test_should_copy_each_state
1014
+ assert_not_same @copied_machine.states[:parked], @machine.states[:parked]
1015
+ end
1016
+
1017
+ def test_should_update_machine_for_each_state
1018
+ assert_equal @copied_machine, @copied_machine.states[:parked].machine
1019
+ end
1020
+
1021
+ def test_should_not_update_machine_for_original_state
1022
+ assert_equal @machine, @machine.states[:parked].machine
1023
+ end
1024
+
1025
+ def test_should_not_have_the_same_collection_of_events
1026
+ assert_not_same @copied_machine.events, @machine.events
1027
+ end
1028
+
1029
+ def test_should_copy_each_event
1030
+ assert_not_same @copied_machine.events[:ignite], @machine.events[:ignite]
1031
+ end
1032
+
1033
+ def test_should_update_machine_for_each_event
1034
+ assert_equal @copied_machine, @copied_machine.events[:ignite].machine
1035
+ end
1036
+
1037
+ def test_should_not_update_machine_for_original_event
1038
+ assert_equal @machine, @machine.events[:ignite].machine
1039
+ end
1040
+
1041
+ def test_should_not_have_the_same_callbacks
1042
+ assert_not_same @copied_machine.callbacks, @machine.callbacks
1043
+ end
1044
+
1045
+ def test_should_not_have_the_same_before_callbacks
1046
+ assert_not_same @copied_machine.callbacks[:before], @machine.callbacks[:before]
1047
+ end
1048
+
1049
+ def test_should_not_have_the_same_after_callbacks
1050
+ assert_not_same @copied_machine.callbacks[:after], @machine.callbacks[:after]
1051
+ end
1052
+
1053
+ def test_should_not_have_the_same_failure_callbacks
1054
+ assert_not_same @copied_machine.callbacks[:failure], @machine.callbacks[:failure]
1055
+ end
1056
+ end
1057
+
1058
+ class MachineAfterChangingOwnerClassTest < Test::Unit::TestCase
1059
+ def setup
1060
+ @original_class = Class.new
1061
+ @machine = StateMachine::Machine.new(@original_class)
1062
+
1063
+ @new_class = Class.new(@original_class)
1064
+ @new_machine = @machine.clone
1065
+ @new_machine.owner_class = @new_class
1066
+
1067
+ @object = @new_class.new
1068
+ end
1069
+
1070
+ def test_should_update_owner_class
1071
+ assert_equal @new_class, @new_machine.owner_class
1072
+ end
1073
+
1074
+ def test_should_not_change_original_owner_class
1075
+ assert_equal @original_class, @machine.owner_class
1076
+ end
1077
+
1078
+ def test_should_change_the_associated_machine_in_the_new_class
1079
+ assert_equal @new_machine, @new_class.state_machines[:state]
1080
+ end
1081
+
1082
+ def test_should_not_change_the_associated_machine_in_the_original_class
1083
+ assert_equal @machine, @original_class.state_machines[:state]
1084
+ end
1085
+ end
1086
+
1087
+ class MachineAfterChangingInitialState < Test::Unit::TestCase
1088
+ def setup
1089
+ @klass = Class.new
1090
+ @machine = StateMachine::Machine.new(@klass, :initial => :parked)
1091
+ @machine.initial_state = :idling
1092
+
1093
+ @object = @klass.new
1094
+ end
1095
+
1096
+ def test_should_change_the_initial_state
1097
+ assert_equal :idling, @machine.initial_state(@object).name
1098
+ end
1099
+
1100
+ def test_should_include_in_known_states
1101
+ assert_equal [:parked, :idling], @machine.states.map {|state| state.name}
1102
+ end
1103
+
1104
+ def test_should_reset_original_initial_state
1105
+ assert !@machine.state(:parked).initial
1106
+ end
1107
+
1108
+ def test_should_set_new_state_to_initial
1109
+ assert @machine.state(:idling).initial
1110
+ end
1111
+ end
1112
+
1113
+ class MachineWithHelpersTest < Test::Unit::TestCase
1114
+ def setup
1115
+ @klass = Class.new
1116
+ @machine = StateMachine::Machine.new(@klass)
1117
+ @object = @klass.new
1118
+ end
1119
+
1120
+ def test_should_throw_exception_with_invalid_scope
1121
+ assert_raise(RUBY_VERSION < '1.9' ? IndexError : KeyError) { @machine.define_helper(:invalid, :park) {} }
1122
+ end
1123
+ end
1124
+
1125
+ class MachineWithInstanceHelpersTest < Test::Unit::TestCase
1126
+ def setup
1127
+ @klass = Class.new
1128
+ @machine = StateMachine::Machine.new(@klass)
1129
+ @object = @klass.new
1130
+ end
1131
+
1132
+ def test_should_not_redefine_existing_public_methods
1133
+ @klass.class_eval do
1134
+ def park
1135
+ true
1136
+ end
1137
+ end
1138
+
1139
+ @machine.define_helper(:instance, :park) {}
1140
+ assert_equal true, @object.park
1141
+ end
1142
+
1143
+ def test_should_not_redefine_existing_protected_methods
1144
+ @klass.class_eval do
1145
+ protected
1146
+ def park
1147
+ true
1148
+ end
1149
+ end
1150
+
1151
+ @machine.define_helper(:instance, :park) {}
1152
+ assert_equal true, @object.send(:park)
1153
+ end
1154
+
1155
+ def test_should_not_redefine_existing_private_methods
1156
+ @klass.class_eval do
1157
+ private
1158
+ def park
1159
+ true
1160
+ end
1161
+ end
1162
+
1163
+ @machine.define_helper(:instance, :park) {}
1164
+ assert_equal true, @object.send(:park)
1165
+ end
1166
+
1167
+ def test_should_warn_if_defined_in_superclass
1168
+ require 'stringio'
1169
+ @original_stderr, $stderr = $stderr, StringIO.new
1170
+
1171
+ superclass = Class.new do
1172
+ def park
1173
+ end
1174
+ end
1175
+ klass = Class.new(superclass)
1176
+ machine = StateMachine::Machine.new(klass)
1177
+
1178
+ machine.define_helper(:instance, :park) {}
1179
+ assert_equal "Instance method \"park\" is already defined in #{superclass.to_s}, use generic helper instead or set StateMachine::Machine.ignore_method_conflicts = true.\n", $stderr.string
1180
+ ensure
1181
+ $stderr = @original_stderr
1182
+ end
1183
+
1184
+ def test_should_warn_if_defined_in_multiple_superclasses
1185
+ require 'stringio'
1186
+ @original_stderr, $stderr = $stderr, StringIO.new
1187
+
1188
+ superclass1 = Class.new do
1189
+ def park
1190
+ end
1191
+ end
1192
+ superclass2 = Class.new(superclass1) do
1193
+ def park
1194
+ end
1195
+ end
1196
+ klass = Class.new(superclass2)
1197
+ machine = StateMachine::Machine.new(klass)
1198
+
1199
+ machine.define_helper(:instance, :park) {}
1200
+ assert_equal "Instance method \"park\" is already defined in #{superclass1.to_s}, use generic helper instead or set StateMachine::Machine.ignore_method_conflicts = true.\n", $stderr.string
1201
+ ensure
1202
+ $stderr = @original_stderr
1203
+ end
1204
+
1205
+ def test_should_warn_if_defined_in_module_prior_to_helper_module
1206
+ require 'stringio'
1207
+ @original_stderr, $stderr = $stderr, StringIO.new
1208
+
1209
+ mod = Module.new do
1210
+ def park
1211
+ end
1212
+ end
1213
+ klass = Class.new do
1214
+ include mod
1215
+ end
1216
+ machine = StateMachine::Machine.new(klass)
1217
+
1218
+ machine.define_helper(:instance, :park) {}
1219
+ assert_equal "Instance method \"park\" is already defined in #{mod.to_s}, use generic helper instead or set StateMachine::Machine.ignore_method_conflicts = true.\n", $stderr.string
1220
+ ensure
1221
+ $stderr = @original_stderr
1222
+ end
1223
+
1224
+ def test_should_not_warn_if_defined_in_module_after_helper_module
1225
+ require 'stringio'
1226
+ @original_stderr, $stderr = $stderr, StringIO.new
1227
+
1228
+ klass = Class.new
1229
+ machine = StateMachine::Machine.new(klass)
1230
+
1231
+ mod = Module.new do
1232
+ def park
1233
+ end
1234
+ end
1235
+ klass.class_eval do
1236
+ include mod
1237
+ end
1238
+
1239
+ machine.define_helper(:instance, :park) {}
1240
+ assert_equal '', $stderr.string
1241
+ ensure
1242
+ $stderr = @original_stderr
1243
+ end
1244
+
1245
+ def test_should_define_if_ignoring_method_conflicts_and_defined_in_superclass
1246
+ require 'stringio'
1247
+ @original_stderr, $stderr = $stderr, StringIO.new
1248
+ StateMachine::Machine.ignore_method_conflicts = true
1249
+
1250
+ superclass = Class.new do
1251
+ def park
1252
+ end
1253
+ end
1254
+ klass = Class.new(superclass)
1255
+ machine = StateMachine::Machine.new(klass)
1256
+
1257
+ machine.define_helper(:instance, :park) {true}
1258
+ assert_equal '', $stderr.string
1259
+ assert_equal true, klass.new.park
1260
+ ensure
1261
+ StateMachine::Machine.ignore_method_conflicts = false
1262
+ $stderr = @original_stderr
1263
+ end
1264
+
1265
+ def test_should_define_nonexistent_methods
1266
+ @machine.define_helper(:instance, :park) {false}
1267
+ assert_equal false, @object.park
1268
+ end
1269
+
1270
+ def test_should_warn_if_defined_multiple_times
1271
+ require 'stringio'
1272
+ @original_stderr, $stderr = $stderr, StringIO.new
1273
+
1274
+ @machine.define_helper(:instance, :park) {}
1275
+ @machine.define_helper(:instance, :park) {}
1276
+
1277
+ assert_equal "Instance method \"park\" is already defined in #{@klass} :state instance helpers, use generic helper instead or set StateMachine::Machine.ignore_method_conflicts = true.\n", $stderr.string
1278
+ ensure
1279
+ $stderr = @original_stderr
1280
+ end
1281
+
1282
+ def test_should_pass_context_as_arguments
1283
+ helper_args = nil
1284
+ @machine.define_helper(:instance, :park) {|*args| helper_args = args}
1285
+ @object.park
1286
+ assert_equal 2, helper_args.length
1287
+ assert_equal [@machine, @object], helper_args
1288
+ end
1289
+
1290
+ def test_should_pass_method_arguments_through
1291
+ helper_args = nil
1292
+ @machine.define_helper(:instance, :park) {|*args| helper_args = args}
1293
+ @object.park(1, 2, 3)
1294
+ assert_equal 5, helper_args.length
1295
+ assert_equal [@machine, @object, 1, 2, 3], helper_args
1296
+ end
1297
+
1298
+ def test_should_allow_string_evaluation
1299
+ @machine.define_helper :instance, <<-end_eval, __FILE__, __LINE__ + 1
1300
+ def park
1301
+ false
1302
+ end
1303
+ end_eval
1304
+ assert_equal false, @object.park
1305
+ end
1306
+ end
1307
+
1308
+ class MachineWithClassHelpersTest < Test::Unit::TestCase
1309
+ def setup
1310
+ @klass = Class.new
1311
+ @machine = StateMachine::Machine.new(@klass)
1312
+ end
1313
+
1314
+ def test_should_not_redefine_existing_public_methods
1315
+ class << @klass
1316
+ def states
1317
+ []
1318
+ end
1319
+ end
1320
+
1321
+ @machine.define_helper(:class, :states) {}
1322
+ assert_equal [], @klass.states
1323
+ end
1324
+
1325
+ def test_should_not_redefine_existing_protected_methods
1326
+ class << @klass
1327
+ protected
1328
+ def states
1329
+ []
1330
+ end
1331
+ end
1332
+
1333
+ @machine.define_helper(:class, :states) {}
1334
+ assert_equal [], @klass.send(:states)
1335
+ end
1336
+
1337
+ def test_should_not_redefine_existing_private_methods
1338
+ class << @klass
1339
+ private
1340
+ def states
1341
+ []
1342
+ end
1343
+ end
1344
+
1345
+ @machine.define_helper(:class, :states) {}
1346
+ assert_equal [], @klass.send(:states)
1347
+ end
1348
+
1349
+ def test_should_warn_if_defined_in_superclass
1350
+ require 'stringio'
1351
+ @original_stderr, $stderr = $stderr, StringIO.new
1352
+
1353
+ superclass = Class.new do
1354
+ def self.park
1355
+ end
1356
+ end
1357
+ klass = Class.new(superclass)
1358
+ machine = StateMachine::Machine.new(klass)
1359
+
1360
+ machine.define_helper(:class, :park) {}
1361
+ assert_equal "Class method \"park\" is already defined in #{superclass.to_s}, use generic helper instead or set StateMachine::Machine.ignore_method_conflicts = true.\n", $stderr.string
1362
+ ensure
1363
+ $stderr = @original_stderr
1364
+ end
1365
+
1366
+ def test_should_warn_if_defined_in_multiple_superclasses
1367
+ require 'stringio'
1368
+ @original_stderr, $stderr = $stderr, StringIO.new
1369
+
1370
+ superclass1 = Class.new do
1371
+ def self.park
1372
+ end
1373
+ end
1374
+ superclass2 = Class.new(superclass1) do
1375
+ def self.park
1376
+ end
1377
+ end
1378
+ klass = Class.new(superclass2)
1379
+ machine = StateMachine::Machine.new(klass)
1380
+
1381
+ machine.define_helper(:class, :park) {}
1382
+ assert_equal "Class method \"park\" is already defined in #{superclass1.to_s}, use generic helper instead or set StateMachine::Machine.ignore_method_conflicts = true.\n", $stderr.string
1383
+ ensure
1384
+ $stderr = @original_stderr
1385
+ end
1386
+
1387
+ def test_should_warn_if_defined_in_module_prior_to_helper_module
1388
+ require 'stringio'
1389
+ @original_stderr, $stderr = $stderr, StringIO.new
1390
+
1391
+ mod = Module.new do
1392
+ def park
1393
+ end
1394
+ end
1395
+ klass = Class.new do
1396
+ extend mod
1397
+ end
1398
+ machine = StateMachine::Machine.new(klass)
1399
+
1400
+ machine.define_helper(:class, :park) {}
1401
+ assert_equal "Class method \"park\" is already defined in #{mod.to_s}, use generic helper instead or set StateMachine::Machine.ignore_method_conflicts = true.\n", $stderr.string
1402
+ ensure
1403
+ $stderr = @original_stderr
1404
+ end
1405
+
1406
+ def test_should_not_warn_if_defined_in_module_after_helper_module
1407
+ require 'stringio'
1408
+ @original_stderr, $stderr = $stderr, StringIO.new
1409
+
1410
+ klass = Class.new
1411
+ machine = StateMachine::Machine.new(klass)
1412
+
1413
+ mod = Module.new do
1414
+ def park
1415
+ end
1416
+ end
1417
+ klass.class_eval do
1418
+ extend mod
1419
+ end
1420
+
1421
+ machine.define_helper(:class, :park) {}
1422
+ assert_equal '', $stderr.string
1423
+ ensure
1424
+ $stderr = @original_stderr
1425
+ end
1426
+
1427
+ def test_should_define_if_ignoring_method_conflicts_and_defined_in_superclass
1428
+ require 'stringio'
1429
+ @original_stderr, $stderr = $stderr, StringIO.new
1430
+ StateMachine::Machine.ignore_method_conflicts = true
1431
+
1432
+ superclass = Class.new do
1433
+ def self.park
1434
+ end
1435
+ end
1436
+ klass = Class.new(superclass)
1437
+ machine = StateMachine::Machine.new(klass)
1438
+
1439
+ machine.define_helper(:class, :park) {true}
1440
+ assert_equal '', $stderr.string
1441
+ assert_equal true, klass.park
1442
+ ensure
1443
+ StateMachine::Machine.ignore_method_conflicts = false
1444
+ $stderr = @original_stderr
1445
+ end
1446
+
1447
+ def test_should_define_nonexistent_methods
1448
+ @machine.define_helper(:class, :states) {[]}
1449
+ assert_equal [], @klass.states
1450
+ end
1451
+
1452
+ def test_should_warn_if_defined_multiple_times
1453
+ require 'stringio'
1454
+ @original_stderr, $stderr = $stderr, StringIO.new
1455
+
1456
+ @machine.define_helper(:class, :states) {}
1457
+ @machine.define_helper(:class, :states) {}
1458
+
1459
+ assert_equal "Class method \"states\" is already defined in #{@klass} :state class helpers, use generic helper instead or set StateMachine::Machine.ignore_method_conflicts = true.\n", $stderr.string
1460
+ ensure
1461
+ $stderr = @original_stderr
1462
+ end
1463
+
1464
+ def test_should_pass_context_as_arguments
1465
+ helper_args = nil
1466
+ @machine.define_helper(:class, :states) {|*args| helper_args = args}
1467
+ @klass.states
1468
+ assert_equal 2, helper_args.length
1469
+ assert_equal [@machine, @klass], helper_args
1470
+ end
1471
+
1472
+ def test_should_pass_method_arguments_through
1473
+ helper_args = nil
1474
+ @machine.define_helper(:class, :states) {|*args| helper_args = args}
1475
+ @klass.states(1, 2, 3)
1476
+ assert_equal 5, helper_args.length
1477
+ assert_equal [@machine, @klass, 1, 2, 3], helper_args
1478
+ end
1479
+
1480
+ def test_should_allow_string_evaluation
1481
+ @machine.define_helper :class, <<-end_eval, __FILE__, __LINE__ + 1
1482
+ def states
1483
+ []
1484
+ end
1485
+ end_eval
1486
+ assert_equal [], @klass.states
1487
+ end
1488
+ end
1489
+
1490
+ class MachineWithConflictingHelpersBeforeDefinitionTest < Test::Unit::TestCase
1491
+ def setup
1492
+ require 'stringio'
1493
+ @original_stderr, $stderr = $stderr, StringIO.new
1494
+
1495
+ @superclass = Class.new do
1496
+ def self.with_state
1497
+ :with_state
1498
+ end
1499
+
1500
+ def self.with_states
1501
+ :with_states
1502
+ end
1503
+
1504
+ def self.without_state
1505
+ :without_state
1506
+ end
1507
+
1508
+ def self.without_states
1509
+ :without_states
1510
+ end
1511
+
1512
+ def self.human_state_name
1513
+ :human_state_name
1514
+ end
1515
+
1516
+ def self.human_state_event_name
1517
+ :human_state_event_name
1518
+ end
1519
+
1520
+ attr_accessor :status
1521
+
1522
+ def state
1523
+ 'parked'
1524
+ end
1525
+
1526
+ def state=(value)
1527
+ self.status = value
1528
+ end
1529
+
1530
+ def state?
1531
+ true
1532
+ end
1533
+
1534
+ def state_name
1535
+ :parked
1536
+ end
1537
+
1538
+ def human_state_name
1539
+ 'parked'
1540
+ end
1541
+
1542
+ def state_events
1543
+ [:ignite]
1544
+ end
1545
+
1546
+ def state_transitions
1547
+ [{:parked => :idling}]
1548
+ end
1549
+
1550
+ def state_paths
1551
+ [[{:parked => :idling}]]
1552
+ end
1553
+
1554
+ def fire_state_event
1555
+ true
1556
+ end
1557
+ end
1558
+ @klass = Class.new(@superclass)
1559
+
1560
+ StateMachine::Integrations.const_set('Custom', Module.new do
1561
+ include StateMachine::Integrations::Base
1562
+
1563
+ def create_with_scope(name)
1564
+ lambda {|klass, values| []}
1565
+ end
1566
+
1567
+ def create_without_scope(name)
1568
+ lambda {|klass, values| []}
1569
+ end
1570
+ end)
1571
+
1572
+ @machine = StateMachine::Machine.new(@klass, :integration => :custom)
1573
+ @machine.state :parked, :idling
1574
+ @machine.event :ignite
1575
+ @object = @klass.new
1576
+ end
1577
+
1578
+ def test_should_not_redefine_singular_with_scope
1579
+ assert_equal :with_state, @klass.with_state
1580
+ end
1581
+
1582
+ def test_should_not_redefine_plural_with_scope
1583
+ assert_equal :with_states, @klass.with_states
1584
+ end
1585
+
1586
+ def test_should_not_redefine_singular_without_scope
1587
+ assert_equal :without_state, @klass.without_state
1588
+ end
1589
+
1590
+ def test_should_not_redefine_plural_without_scope
1591
+ assert_equal :without_states, @klass.without_states
1592
+ end
1593
+
1594
+ def test_should_not_redefine_human_attribute_name_reader
1595
+ assert_equal :human_state_name, @klass.human_state_name
1596
+ end
1597
+
1598
+ def test_should_not_redefine_human_event_name_reader
1599
+ assert_equal :human_state_event_name, @klass.human_state_event_name
1600
+ end
1601
+
1602
+ def test_should_not_redefine_attribute_reader
1603
+ assert_equal 'parked', @object.state
1604
+ end
1605
+
1606
+ def test_should_not_redefine_attribute_writer
1607
+ @object.state = 'parked'
1608
+ assert_equal 'parked', @object.status
1609
+ end
1610
+
1611
+ def test_should_not_define_attribute_predicate
1612
+ assert @object.state?
1613
+ end
1614
+
1615
+ def test_should_not_redefine_attribute_name_reader
1616
+ assert_equal :parked, @object.state_name
1617
+ end
1618
+
1619
+ def test_should_not_redefine_attribute_human_name_reader
1620
+ assert_equal 'parked', @object.human_state_name
1621
+ end
1622
+
1623
+ def test_should_not_redefine_attribute_events_reader
1624
+ assert_equal [:ignite], @object.state_events
1625
+ end
1626
+
1627
+ def test_should_not_redefine_attribute_transitions_reader
1628
+ assert_equal [{:parked => :idling}], @object.state_transitions
1629
+ end
1630
+
1631
+ def test_should_not_redefine_attribute_paths_reader
1632
+ assert_equal [[{:parked => :idling}]], @object.state_paths
1633
+ end
1634
+
1635
+ def test_should_not_redefine_event_runner
1636
+ assert_equal true, @object.fire_state_event
1637
+ end
1638
+
1639
+ def test_should_output_warning
1640
+ expected = [
1641
+ 'Instance method "state_events"',
1642
+ 'Instance method "state_transitions"',
1643
+ 'Instance method "fire_state_event"',
1644
+ 'Instance method "state_paths"',
1645
+ 'Class method "human_state_name"',
1646
+ 'Class method "human_state_event_name"',
1647
+ 'Instance method "state_name"',
1648
+ 'Instance method "human_state_name"',
1649
+ 'Class method "with_state"',
1650
+ 'Class method "with_states"',
1651
+ 'Class method "without_state"',
1652
+ 'Class method "without_states"'
1653
+ ].map {|method| "#{method} is already defined in #{@superclass.to_s}, use generic helper instead or set StateMachine::Machine.ignore_method_conflicts = true.\n"}.join
1654
+
1655
+ assert_equal expected, $stderr.string
1656
+ end
1657
+
1658
+ def teardown
1659
+ $stderr = @original_stderr
1660
+ StateMachine::Integrations.send(:remove_const, 'Custom')
1661
+ end
1662
+ end
1663
+
1664
+ class MachineWithConflictingHelpersAfterDefinitionTest < Test::Unit::TestCase
1665
+ def setup
1666
+ require 'stringio'
1667
+ @original_stderr, $stderr = $stderr, StringIO.new
1668
+
1669
+ @klass = Class.new do
1670
+ def self.with_state
1671
+ :with_state
1672
+ end
1673
+
1674
+ def self.with_states
1675
+ :with_states
1676
+ end
1677
+
1678
+ def self.without_state
1679
+ :without_state
1680
+ end
1681
+
1682
+ def self.without_states
1683
+ :without_states
1684
+ end
1685
+
1686
+ def self.human_state_name
1687
+ :human_state_name
1688
+ end
1689
+
1690
+ def self.human_state_event_name
1691
+ :human_state_event_name
1692
+ end
1693
+
1694
+ attr_accessor :status
1695
+
1696
+ def state
1697
+ 'parked'
1698
+ end
1699
+
1700
+ def state=(value)
1701
+ self.status = value
1702
+ end
1703
+
1704
+ def state?
1705
+ true
1706
+ end
1707
+
1708
+ def state_name
1709
+ :parked
1710
+ end
1711
+
1712
+ def human_state_name
1713
+ 'parked'
1714
+ end
1715
+
1716
+ def state_events
1717
+ [:ignite]
1718
+ end
1719
+
1720
+ def state_transitions
1721
+ [{:parked => :idling}]
1722
+ end
1723
+
1724
+ def state_paths
1725
+ [[{:parked => :idling}]]
1726
+ end
1727
+
1728
+ def fire_state_event
1729
+ true
1730
+ end
1731
+ end
1732
+
1733
+ StateMachine::Integrations.const_set('Custom', Module.new do
1734
+ include StateMachine::Integrations::Base
1735
+
1736
+ def create_with_scope(name)
1737
+ lambda {|klass, values| []}
1738
+ end
1739
+
1740
+ def create_without_scope(name)
1741
+ lambda {|klass, values| []}
1742
+ end
1743
+ end)
1744
+
1745
+ @machine = StateMachine::Machine.new(@klass, :integration => :custom)
1746
+ @machine.state :parked, :idling
1747
+ @machine.event :ignite
1748
+ @object = @klass.new
1749
+ end
1750
+
1751
+ def test_should_not_redefine_singular_with_scope
1752
+ assert_equal :with_state, @klass.with_state
1753
+ end
1754
+
1755
+ def test_should_not_redefine_plural_with_scope
1756
+ assert_equal :with_states, @klass.with_states
1757
+ end
1758
+
1759
+ def test_should_not_redefine_singular_without_scope
1760
+ assert_equal :without_state, @klass.without_state
1761
+ end
1762
+
1763
+ def test_should_not_redefine_plural_without_scope
1764
+ assert_equal :without_states, @klass.without_states
1765
+ end
1766
+
1767
+ def test_should_not_redefine_human_attribute_name_reader
1768
+ assert_equal :human_state_name, @klass.human_state_name
1769
+ end
1770
+
1771
+ def test_should_not_redefine_human_event_name_reader
1772
+ assert_equal :human_state_event_name, @klass.human_state_event_name
1773
+ end
1774
+
1775
+ def test_should_not_redefine_attribute_reader
1776
+ assert_equal 'parked', @object.state
1777
+ end
1778
+
1779
+ def test_should_not_redefine_attribute_writer
1780
+ @object.state = 'parked'
1781
+ assert_equal 'parked', @object.status
1782
+ end
1783
+
1784
+ def test_should_not_define_attribute_predicate
1785
+ assert @object.state?
1786
+ end
1787
+
1788
+ def test_should_not_redefine_attribute_name_reader
1789
+ assert_equal :parked, @object.state_name
1790
+ end
1791
+
1792
+ def test_should_not_redefine_attribute_human_name_reader
1793
+ assert_equal 'parked', @object.human_state_name
1794
+ end
1795
+
1796
+ def test_should_not_redefine_attribute_events_reader
1797
+ assert_equal [:ignite], @object.state_events
1798
+ end
1799
+
1800
+ def test_should_not_redefine_attribute_transitions_reader
1801
+ assert_equal [{:parked => :idling}], @object.state_transitions
1802
+ end
1803
+
1804
+ def test_should_not_redefine_attribute_paths_reader
1805
+ assert_equal [[{:parked => :idling}]], @object.state_paths
1806
+ end
1807
+
1808
+ def test_should_not_redefine_event_runner
1809
+ assert_equal true, @object.fire_state_event
1810
+ end
1811
+
1812
+ def test_should_allow_super_chaining
1813
+ @klass.class_eval do
1814
+ def self.with_state(*states)
1815
+ super
1816
+ end
1817
+
1818
+ def self.with_states(*states)
1819
+ super
1820
+ end
1821
+
1822
+ def self.without_state(*states)
1823
+ super
1824
+ end
1825
+
1826
+ def self.without_states(*states)
1827
+ super
1828
+ end
1829
+
1830
+ def self.human_state_name(state)
1831
+ super
1832
+ end
1833
+
1834
+ def self.human_state_event_name(event)
1835
+ super
1836
+ end
1837
+
1838
+ attr_accessor :status
1839
+
1840
+ def state
1841
+ super
1842
+ end
1843
+
1844
+ def state=(value)
1845
+ super
1846
+ end
1847
+
1848
+ def state?(state)
1849
+ super
1850
+ end
1851
+
1852
+ def state_name
1853
+ super
1854
+ end
1855
+
1856
+ def human_state_name
1857
+ super
1858
+ end
1859
+
1860
+ def state_events
1861
+ super
1862
+ end
1863
+
1864
+ def state_transitions
1865
+ super
1866
+ end
1867
+
1868
+ def state_paths
1869
+ super
1870
+ end
1871
+
1872
+ def fire_state_event(event)
1873
+ super
1874
+ end
1875
+ end
1876
+
1877
+ assert_equal [], @klass.with_state
1878
+ assert_equal [], @klass.with_states
1879
+ assert_equal [], @klass.without_state
1880
+ assert_equal [], @klass.without_states
1881
+ assert_equal 'parked', @klass.human_state_name(:parked)
1882
+ assert_equal 'ignite', @klass.human_state_event_name(:ignite)
1883
+
1884
+ assert_equal nil, @object.state
1885
+ @object.state = 'idling'
1886
+ assert_equal 'idling', @object.state
1887
+ assert_equal nil, @object.status
1888
+ assert_equal false, @object.state?(:parked)
1889
+ assert_equal :idling, @object.state_name
1890
+ assert_equal 'idling', @object.human_state_name
1891
+ assert_equal [], @object.state_events
1892
+ assert_equal [], @object.state_transitions
1893
+ assert_equal [], @object.state_paths
1894
+ assert_equal false, @object.fire_state_event(:ignite)
1895
+ end
1896
+
1897
+ def test_should_not_output_warning
1898
+ assert_equal '', $stderr.string
1899
+ end
1900
+
1901
+ def teardown
1902
+ $stderr = @original_stderr
1903
+ StateMachine::Integrations.send(:remove_const, 'Custom')
1904
+ end
1905
+ end
1906
+
1907
+ class MachineWithSuperclassConflictingHelpersAfterDefinitionTest < Test::Unit::TestCase
1908
+ def setup
1909
+ require 'stringio'
1910
+ @original_stderr, $stderr = $stderr, StringIO.new
1911
+
1912
+ @superclass = Class.new
1913
+ @klass = Class.new(@superclass)
1914
+
1915
+ @machine = StateMachine::Machine.new(@klass)
1916
+ @machine.state :parked, :idling
1917
+ @machine.event :ignite
1918
+
1919
+ @superclass.class_eval do
1920
+ def state?
1921
+ true
1922
+ end
1923
+ end
1924
+
1925
+ @object = @klass.new
1926
+ end
1927
+
1928
+ def test_should_call_superclass_attribute_predicate_without_arguments
1929
+ assert @object.state?
1930
+ end
1931
+
1932
+ def test_should_define_attribute_predicate_with_arguments
1933
+ assert !@object.state?(:parked)
1934
+ end
1935
+
1936
+ def teardown
1937
+ $stderr = @original_stderr
1938
+ end
1939
+ end
1940
+
1941
+ class MachineWithoutInitializeTest < Test::Unit::TestCase
1942
+ def setup
1943
+ @klass = Class.new
1944
+ @machine = StateMachine::Machine.new(@klass, :initial => :parked)
1945
+ @object = @klass.new
1946
+ end
1947
+
1948
+ def test_should_initialize_state
1949
+ assert_equal 'parked', @object.state
1950
+ end
1951
+ end
1952
+
1953
+ class MachineWithInitializeWithoutSuperTest < Test::Unit::TestCase
1954
+ def setup
1955
+ @klass = Class.new do
1956
+ def initialize
1957
+ end
1958
+ end
1959
+ @machine = StateMachine::Machine.new(@klass, :initial => :parked)
1960
+ @object = @klass.new
1961
+ end
1962
+
1963
+ def test_should_not_initialize_state
1964
+ assert_nil @object.state
1965
+ end
1966
+ end
1967
+
1968
+ class MachineWithInitializeAndSuperTest < Test::Unit::TestCase
1969
+ def setup
1970
+ @klass = Class.new do
1971
+ def initialize
1972
+ super()
1973
+ end
1974
+ end
1975
+ @machine = StateMachine::Machine.new(@klass, :initial => :parked)
1976
+ @object = @klass.new
1977
+ end
1978
+
1979
+ def test_should_initialize_state
1980
+ assert_equal 'parked', @object.state
1981
+ end
1982
+ end
1983
+
1984
+ class MachineWithInitializeArgumentsAndBlockTest < Test::Unit::TestCase
1985
+ def setup
1986
+ @superclass = Class.new do
1987
+ attr_reader :args
1988
+ attr_reader :block_given
1989
+
1990
+ def initialize(*args)
1991
+ @args = args
1992
+ @block_given = block_given?
1993
+ end
1994
+ end
1995
+ @klass = Class.new(@superclass)
1996
+ @machine = StateMachine::Machine.new(@klass, :initial => :parked)
1997
+ @object = @klass.new(1, 2, 3) {}
1998
+ end
1999
+
2000
+ def test_should_initialize_state
2001
+ assert_equal 'parked', @object.state
2002
+ end
2003
+
2004
+ def test_should_preserve_arguments
2005
+ assert_equal [1, 2, 3], @object.args
2006
+ end
2007
+
2008
+ def test_should_preserve_block
2009
+ assert @object.block_given
2010
+ end
2011
+ end
2012
+
2013
+ class MachineWithCustomInitializeTest < Test::Unit::TestCase
2014
+ def setup
2015
+ @klass = Class.new do
2016
+ def initialize(state = nil, options = {})
2017
+ @state = state
2018
+ initialize_state_machines(options)
2019
+ end
2020
+ end
2021
+ @machine = StateMachine::Machine.new(@klass, :initial => :parked)
2022
+ @object = @klass.new
2023
+ end
2024
+
2025
+ def test_should_initialize_state
2026
+ assert_equal 'parked', @object.state
2027
+ end
2028
+
2029
+ def test_should_allow_custom_options
2030
+ @machine.state :idling
2031
+ @object = @klass.new('idling', :static => :force)
2032
+ assert_equal 'parked', @object.state
2033
+ end
2034
+ end
2035
+
2036
+ class MachinePersistenceTest < Test::Unit::TestCase
2037
+ def setup
2038
+ @klass = Class.new do
2039
+ attr_accessor :state_event
2040
+ end
2041
+ @machine = StateMachine::Machine.new(@klass, :initial => :parked)
2042
+ @object = @klass.new
2043
+ end
2044
+
2045
+ def test_should_allow_reading_state
2046
+ assert_equal 'parked', @machine.read(@object, :state)
2047
+ end
2048
+
2049
+ def test_should_allow_reading_custom_attributes
2050
+ assert_nil @machine.read(@object, :event)
2051
+
2052
+ @object.state_event = 'ignite'
2053
+ assert_equal 'ignite', @machine.read(@object, :event)
2054
+ end
2055
+
2056
+ def test_should_allow_reading_custom_instance_variables
2057
+ @klass.class_eval do
2058
+ attr_writer :state_value
2059
+ end
2060
+
2061
+ @object.state_value = 1
2062
+ assert_raise(NoMethodError) { @machine.read(@object, :value) }
2063
+ assert_equal 1, @machine.read(@object, :value, true)
2064
+ end
2065
+
2066
+ def test_should_allow_writing_state
2067
+ @machine.write(@object, :state, 'idling')
2068
+ assert_equal 'idling', @object.state
2069
+ end
2070
+
2071
+ def test_should_allow_writing_custom_attributes
2072
+ @machine.write(@object, :event, 'ignite')
2073
+ assert_equal 'ignite', @object.state_event
2074
+ end
2075
+
2076
+ def test_should_allow_writing_custom_instance_variables
2077
+ @klass.class_eval do
2078
+ attr_reader :state_value
2079
+ end
2080
+
2081
+ assert_raise(NoMethodError) { @machine.write(@object, :value, 1) }
2082
+ assert_equal 1, @machine.write(@object, :value, 1, true)
2083
+ assert_equal 1, @object.state_value
2084
+ end
2085
+ end
2086
+
2087
+
2088
+ class MachineWithStatesTest < Test::Unit::TestCase
2089
+ def setup
2090
+ @klass = Class.new
2091
+ @machine = StateMachine::Machine.new(@klass)
2092
+ @parked, @idling = @machine.state :parked, :idling
2093
+
2094
+ @object = @klass.new
2095
+ end
2096
+
2097
+ def test_should_have_states
2098
+ assert_equal [nil, :parked, :idling], @machine.states.map {|state| state.name}
2099
+ end
2100
+
2101
+ def test_should_allow_state_lookup_by_name
2102
+ assert_equal @parked, @machine.states[:parked]
2103
+ end
2104
+
2105
+ def test_should_allow_state_lookup_by_value
2106
+ assert_equal @parked, @machine.states['parked', :value]
2107
+ end
2108
+
2109
+ def test_should_allow_human_state_name_lookup
2110
+ assert_equal 'parked', @klass.human_state_name(:parked)
2111
+ end
2112
+
2113
+ def test_should_raise_exception_on_invalid_human_state_name_lookup
2114
+ exception = assert_raise(IndexError) {@klass.human_state_name(:invalid)}
2115
+ assert_equal ':invalid is an invalid name', exception.message
2116
+ end
2117
+
2118
+ def test_should_use_stringified_name_for_value
2119
+ assert_equal 'parked', @parked.value
2120
+ end
2121
+
2122
+ def test_should_not_use_custom_matcher
2123
+ assert_nil @parked.matcher
2124
+ end
2125
+
2126
+ def test_should_raise_exception_if_invalid_option_specified
2127
+ exception = assert_raise(ArgumentError) {@machine.state(:first_gear, :invalid => true)}
2128
+ assert_equal 'Invalid key(s): invalid', exception.message
2129
+ end
2130
+
2131
+ def test_should_raise_exception_if_conflicting_type_used_for_name
2132
+ exception = assert_raise(ArgumentError) { @machine.state 'first_gear' }
2133
+ assert_equal '"first_gear" state defined as String, :parked defined as Symbol; all states must be consistent', exception.message
2134
+ end
2135
+
2136
+ def test_should_not_raise_exception_if_conflicting_type_is_nil_for_name
2137
+ assert_nothing_raised { @machine.state nil }
2138
+ end
2139
+ end
2140
+
2141
+ class MachineWithStatesWithCustomValuesTest < Test::Unit::TestCase
2142
+ def setup
2143
+ @klass = Class.new
2144
+ @machine = StateMachine::Machine.new(@klass)
2145
+ @state = @machine.state :parked, :value => 1
2146
+
2147
+ @object = @klass.new
2148
+ @object.state = 1
2149
+ end
2150
+
2151
+ def test_should_use_custom_value
2152
+ assert_equal 1, @state.value
2153
+ end
2154
+
2155
+ def test_should_allow_lookup_by_custom_value
2156
+ assert_equal @state, @machine.states[1, :value]
2157
+ end
2158
+ end
2159
+
2160
+ class MachineWithStatesWithCustomHumanNamesTest < Test::Unit::TestCase
2161
+ def setup
2162
+ @klass = Class.new
2163
+ @machine = StateMachine::Machine.new(@klass)
2164
+ @state = @machine.state :parked, :human_name => 'stopped'
2165
+ end
2166
+
2167
+ def test_should_use_custom_human_name
2168
+ assert_equal 'stopped', @state.human_name
2169
+ end
2170
+
2171
+ def test_should_allow_human_state_name_lookup
2172
+ assert_equal 'stopped', @klass.human_state_name(:parked)
2173
+ end
2174
+ end
2175
+
2176
+ class MachineWithStatesWithRuntimeDependenciesTest < Test::Unit::TestCase
2177
+ def setup
2178
+ @klass = Class.new
2179
+ @machine = StateMachine::Machine.new(@klass)
2180
+ @machine.state :parked
2181
+ end
2182
+
2183
+ def test_should_not_evaluate_value_during_definition
2184
+ assert_nothing_raised { @machine.state :parked, :value => lambda {raise ArgumentError} }
2185
+ end
2186
+
2187
+ def test_should_not_evaluate_if_not_initial_state
2188
+ @machine.state :parked, :value => lambda {raise ArgumentError}
2189
+ assert_nothing_raised { @klass.new }
2190
+ end
2191
+ end
2192
+
2193
+ class MachineWithStateWithMatchersTest < Test::Unit::TestCase
2194
+ def setup
2195
+ @klass = Class.new
2196
+ @machine = StateMachine::Machine.new(@klass)
2197
+ @state = @machine.state :parked, :if => lambda {|value| !value.nil?}
2198
+
2199
+ @object = @klass.new
2200
+ @object.state = 1
2201
+ end
2202
+
2203
+ def test_should_use_custom_matcher
2204
+ assert_not_nil @state.matcher
2205
+ assert @state.matches?(1)
2206
+ assert !@state.matches?(nil)
2207
+ end
2208
+ end
2209
+
2210
+ class MachineWithCachedStateTest < Test::Unit::TestCase
2211
+ def setup
2212
+ @klass = Class.new
2213
+ @machine = StateMachine::Machine.new(@klass, :initial => :parked)
2214
+ @state = @machine.state :parked, :value => lambda {Object.new}, :cache => true
2215
+
2216
+ @object = @klass.new
2217
+ end
2218
+
2219
+ def test_should_use_evaluated_value
2220
+ assert_instance_of Object, @object.state
2221
+ end
2222
+
2223
+ def test_use_same_value_across_multiple_objects
2224
+ assert_equal @object.state, @klass.new.state
2225
+ end
2226
+ end
2227
+
2228
+ class MachineWithStatesWithBehaviorsTest < Test::Unit::TestCase
2229
+ def setup
2230
+ @klass = Class.new
2231
+ @machine = StateMachine::Machine.new(@klass)
2232
+
2233
+ @parked, @idling = @machine.state :parked, :idling do
2234
+ def speed
2235
+ 0
2236
+ end
2237
+ end
2238
+ end
2239
+
2240
+ def test_should_define_behaviors_for_each_state
2241
+ assert_not_nil @parked.context_methods[:speed]
2242
+ assert_not_nil @idling.context_methods[:speed]
2243
+ end
2244
+
2245
+ def test_should_define_different_behaviors_for_each_state
2246
+ assert_not_equal @parked.context_methods[:speed], @idling.context_methods[:speed]
2247
+ end
2248
+ end
2249
+
2250
+ class MachineWithExistingStateTest < Test::Unit::TestCase
2251
+ def setup
2252
+ @klass = Class.new
2253
+ @machine = StateMachine::Machine.new(@klass)
2254
+ @state = @machine.state :parked
2255
+ @same_state = @machine.state :parked, :value => 1
2256
+ end
2257
+
2258
+ def test_should_not_create_a_new_state
2259
+ assert_same @state, @same_state
2260
+ end
2261
+
2262
+ def test_should_update_attributes
2263
+ assert_equal 1, @state.value
2264
+ end
2265
+
2266
+ def test_should_no_longer_be_able_to_look_up_state_by_original_value
2267
+ assert_nil @machine.states['parked', :value]
2268
+ end
2269
+
2270
+ def test_should_be_able_to_look_up_state_by_new_value
2271
+ assert_equal @state, @machine.states[1, :value]
2272
+ end
2273
+ end
2274
+
2275
+ class MachineWithStateMatchersTest < Test::Unit::TestCase
2276
+ def setup
2277
+ @klass = Class.new
2278
+ @machine = StateMachine::Machine.new(@klass)
2279
+ end
2280
+
2281
+ def test_should_empty_array_for_all_matcher
2282
+ assert_equal [], @machine.state(StateMachine::AllMatcher.instance)
2283
+ end
2284
+
2285
+ def test_should_return_referenced_states_for_blacklist_matcher
2286
+ assert_instance_of StateMachine::State, @machine.state(StateMachine::BlacklistMatcher.new([:parked]))
2287
+ end
2288
+
2289
+ def test_should_not_allow_configurations
2290
+ exception = assert_raise(ArgumentError) { @machine.state(StateMachine::BlacklistMatcher.new([:parked]), :human_name => 'Parked') }
2291
+ assert_equal 'Cannot configure states when using matchers (using {:human_name=>"Parked"})', exception.message
2292
+ end
2293
+
2294
+ def test_should_track_referenced_states
2295
+ @machine.state(StateMachine::BlacklistMatcher.new([:parked]))
2296
+ assert_equal [nil, :parked], @machine.states.map {|state| state.name}
2297
+ end
2298
+
2299
+ def test_should_eval_context_for_matching_states
2300
+ contexts_run = []
2301
+ @machine.event(StateMachine::BlacklistMatcher.new([:parked])) { contexts_run << self.name }
2302
+
2303
+ @machine.event :parked
2304
+ assert_equal [], contexts_run
2305
+
2306
+ @machine.event :idling
2307
+ assert_equal [:idling], contexts_run
2308
+
2309
+ @machine.event :first_gear, :second_gear
2310
+ assert_equal [:idling, :first_gear, :second_gear], contexts_run
2311
+ end
2312
+ end
2313
+
2314
+ class MachineWithOtherStates < Test::Unit::TestCase
2315
+ def setup
2316
+ @klass = Class.new
2317
+ @machine = StateMachine::Machine.new(@klass, :initial => :parked)
2318
+ @parked, @idling = @machine.other_states(:parked, :idling)
2319
+ end
2320
+
2321
+ def test_should_include_other_states_in_known_states
2322
+ assert_equal [@parked, @idling], @machine.states.to_a
2323
+ end
2324
+
2325
+ def test_should_use_default_value
2326
+ assert_equal 'idling', @idling.value
2327
+ end
2328
+
2329
+ def test_should_not_create_matcher
2330
+ assert_nil @idling.matcher
2331
+ end
2332
+ end
2333
+
2334
+ class MachineWithEventsTest < Test::Unit::TestCase
2335
+ def setup
2336
+ @klass = Class.new
2337
+ @machine = StateMachine::Machine.new(@klass)
2338
+ end
2339
+
2340
+ def test_should_return_the_created_event
2341
+ assert_instance_of StateMachine::Event, @machine.event(:ignite)
2342
+ end
2343
+
2344
+ def test_should_create_event_with_given_name
2345
+ event = @machine.event(:ignite) {}
2346
+ assert_equal :ignite, event.name
2347
+ end
2348
+
2349
+ def test_should_evaluate_block_within_event_context
2350
+ responded = false
2351
+ @machine.event :ignite do
2352
+ responded = respond_to?(:transition)
2353
+ end
2354
+
2355
+ assert responded
2356
+ end
2357
+
2358
+ def test_should_be_aliased_as_on
2359
+ event = @machine.on(:ignite) {}
2360
+ assert_equal :ignite, event.name
2361
+ end
2362
+
2363
+ def test_should_have_events
2364
+ event = @machine.event(:ignite)
2365
+ assert_equal [event], @machine.events.to_a
2366
+ end
2367
+
2368
+ def test_should_allow_human_state_name_lookup
2369
+ @machine.event(:ignite)
2370
+ assert_equal 'ignite', @klass.human_state_event_name(:ignite)
2371
+ end
2372
+
2373
+ def test_should_raise_exception_on_invalid_human_state_event_name_lookup
2374
+ exception = assert_raise(IndexError) {@klass.human_state_event_name(:invalid)}
2375
+ assert_equal ':invalid is an invalid name', exception.message
2376
+ end
2377
+
2378
+ def test_should_raise_exception_if_conflicting_type_used_for_name
2379
+ @machine.event :park
2380
+ exception = assert_raise(ArgumentError) { @machine.event 'ignite' }
2381
+ assert_equal '"ignite" event defined as String, :park defined as Symbol; all events must be consistent', exception.message
2382
+ end
2383
+ end
2384
+
2385
+ class MachineWithExistingEventTest < Test::Unit::TestCase
2386
+ def setup
2387
+ @machine = StateMachine::Machine.new(Class.new)
2388
+ @event = @machine.event(:ignite)
2389
+ @same_event = @machine.event(:ignite)
2390
+ end
2391
+
2392
+ def test_should_not_create_new_event
2393
+ assert_same @event, @same_event
2394
+ end
2395
+
2396
+ def test_should_allow_accessing_event_without_block
2397
+ assert_equal @event, @machine.event(:ignite)
2398
+ end
2399
+ end
2400
+
2401
+ class MachineWithEventsWithCustomHumanNamesTest < Test::Unit::TestCase
2402
+ def setup
2403
+ @klass = Class.new
2404
+ @machine = StateMachine::Machine.new(@klass)
2405
+ @event = @machine.event(:ignite, :human_name => 'start')
2406
+ end
2407
+
2408
+ def test_should_use_custom_human_name
2409
+ assert_equal 'start', @event.human_name
2410
+ end
2411
+
2412
+ def test_should_allow_human_state_name_lookup
2413
+ assert_equal 'start', @klass.human_state_event_name(:ignite)
2414
+ end
2415
+ end
2416
+
2417
+ class MachineWithEventMatchersTest < Test::Unit::TestCase
2418
+ def setup
2419
+ @klass = Class.new
2420
+ @machine = StateMachine::Machine.new(@klass)
2421
+ end
2422
+
2423
+ def test_should_empty_array_for_all_matcher
2424
+ assert_equal [], @machine.event(StateMachine::AllMatcher.instance)
2425
+ end
2426
+
2427
+ def test_should_return_referenced_events_for_blacklist_matcher
2428
+ assert_instance_of StateMachine::Event, @machine.event(StateMachine::BlacklistMatcher.new([:park]))
2429
+ end
2430
+
2431
+ def test_should_not_allow_configurations
2432
+ exception = assert_raise(ArgumentError) { @machine.event(StateMachine::BlacklistMatcher.new([:park]), :human_name => 'Park') }
2433
+ assert_equal 'Cannot configure events when using matchers (using {:human_name=>"Park"})', exception.message
2434
+ end
2435
+
2436
+ def test_should_track_referenced_events
2437
+ @machine.event(StateMachine::BlacklistMatcher.new([:park]))
2438
+ assert_equal [:park], @machine.events.map {|event| event.name}
2439
+ end
2440
+
2441
+ def test_should_eval_context_for_matching_events
2442
+ contexts_run = []
2443
+ @machine.event(StateMachine::BlacklistMatcher.new([:park])) { contexts_run << self.name }
2444
+
2445
+ @machine.event :park
2446
+ assert_equal [], contexts_run
2447
+
2448
+ @machine.event :ignite
2449
+ assert_equal [:ignite], contexts_run
2450
+
2451
+ @machine.event :shift_up, :shift_down
2452
+ assert_equal [:ignite, :shift_up, :shift_down], contexts_run
2453
+ end
2454
+ end
2455
+
2456
+ class MachineWithEventsWithTransitionsTest < Test::Unit::TestCase
2457
+ def setup
2458
+ @klass = Class.new
2459
+ @machine = StateMachine::Machine.new(@klass, :initial => :parked)
2460
+ @event = @machine.event(:ignite) do
2461
+ transition :parked => :idling
2462
+ transition :stalled => :idling
2463
+ end
2464
+ end
2465
+
2466
+ def test_should_have_events
2467
+ assert_equal [@event], @machine.events.to_a
2468
+ end
2469
+
2470
+ def test_should_track_states_defined_in_event_transitions
2471
+ assert_equal [:parked, :idling, :stalled], @machine.states.map {|state| state.name}
2472
+ end
2473
+
2474
+ def test_should_not_duplicate_states_defined_in_multiple_event_transitions
2475
+ @machine.event :park do
2476
+ transition :idling => :parked
2477
+ end
2478
+
2479
+ assert_equal [:parked, :idling, :stalled], @machine.states.map {|state| state.name}
2480
+ end
2481
+
2482
+ def test_should_track_state_from_new_events
2483
+ @machine.event :shift_up do
2484
+ transition :idling => :first_gear
2485
+ end
2486
+
2487
+ assert_equal [:parked, :idling, :stalled, :first_gear], @machine.states.map {|state| state.name}
2488
+ end
2489
+ end
2490
+
2491
+ class MachineWithMultipleEventsTest < Test::Unit::TestCase
2492
+ def setup
2493
+ @klass = Class.new
2494
+ @machine = StateMachine::Machine.new(@klass, :initial => :parked)
2495
+ @park, @shift_down = @machine.event(:park, :shift_down) do
2496
+ transition :first_gear => :parked
2497
+ end
2498
+ end
2499
+
2500
+ def test_should_have_events
2501
+ assert_equal [@park, @shift_down], @machine.events.to_a
2502
+ end
2503
+
2504
+ def test_should_define_transitions_for_each_event
2505
+ [@park, @shift_down].each {|event| assert_equal 1, event.branches.size}
2506
+ end
2507
+
2508
+ def test_should_transition_the_same_for_each_event
2509
+ object = @klass.new
2510
+ object.state = 'first_gear'
2511
+ object.park
2512
+ assert_equal 'parked', object.state
2513
+
2514
+ object = @klass.new
2515
+ object.state = 'first_gear'
2516
+ object.shift_down
2517
+ assert_equal 'parked', object.state
2518
+ end
2519
+ end
2520
+
2521
+ class MachineWithTransitionsTest < Test::Unit::TestCase
2522
+ def setup
2523
+ @klass = Class.new
2524
+ @machine = StateMachine::Machine.new(@klass, :initial => :parked)
2525
+ end
2526
+
2527
+ def test_should_require_on_event
2528
+ exception = assert_raise(ArgumentError) { @machine.transition(:parked => :idling) }
2529
+ assert_equal 'Must specify :on event', exception.message
2530
+ end
2531
+
2532
+ def test_should_not_allow_except_on_option
2533
+ exception = assert_raise(ArgumentError) {@machine.transition(:except_on => :ignite, :on => :ignite)}
2534
+ assert_equal 'Invalid key(s): except_on', exception.message
2535
+ end
2536
+
2537
+ def test_should_allow_transitioning_without_a_to_state
2538
+ assert_nothing_raised {@machine.transition(:from => :parked, :on => :ignite)}
2539
+ end
2540
+
2541
+ def test_should_allow_transitioning_without_a_from_state
2542
+ assert_nothing_raised {@machine.transition(:to => :idling, :on => :ignite)}
2543
+ end
2544
+
2545
+ def test_should_allow_except_from_option
2546
+ assert_nothing_raised {@machine.transition(:except_from => :idling, :on => :ignite)}
2547
+ end
2548
+
2549
+ def test_should_allow_except_to_option
2550
+ assert_nothing_raised {@machine.transition(:except_to => :parked, :on => :ignite)}
2551
+ end
2552
+
2553
+ def test_should_allow_implicit_options
2554
+ branch = @machine.transition(:first_gear => :second_gear, :on => :shift_up)
2555
+ assert_instance_of StateMachine::Branch, branch
2556
+
2557
+ state_requirements = branch.state_requirements
2558
+ assert_equal 1, state_requirements.length
2559
+
2560
+ assert_instance_of StateMachine::WhitelistMatcher, state_requirements[0][:from]
2561
+ assert_equal [:first_gear], state_requirements[0][:from].values
2562
+ assert_instance_of StateMachine::WhitelistMatcher, state_requirements[0][:to]
2563
+ assert_equal [:second_gear], state_requirements[0][:to].values
2564
+ assert_instance_of StateMachine::WhitelistMatcher, branch.event_requirement
2565
+ assert_equal [:shift_up], branch.event_requirement.values
2566
+ end
2567
+
2568
+ def test_should_allow_multiple_implicit_options
2569
+ branch = @machine.transition(:first_gear => :second_gear, :second_gear => :third_gear, :on => :shift_up)
2570
+
2571
+ state_requirements = branch.state_requirements
2572
+ assert_equal 2, state_requirements.length
2573
+ end
2574
+
2575
+ def test_should_allow_verbose_options
2576
+ branch = @machine.transition(:from => :parked, :to => :idling, :on => :ignite)
2577
+ assert_instance_of StateMachine::Branch, branch
2578
+ end
2579
+
2580
+ def test_should_include_all_transition_states_in_machine_states
2581
+ @machine.transition(:parked => :idling, :on => :ignite)
2582
+
2583
+ assert_equal [:parked, :idling], @machine.states.map {|state| state.name}
2584
+ end
2585
+
2586
+ def test_should_include_all_transition_events_in_machine_events
2587
+ @machine.transition(:parked => :idling, :on => :ignite)
2588
+
2589
+ assert_equal [:ignite], @machine.events.map {|event| event.name}
2590
+ end
2591
+
2592
+ def test_should_allow_multiple_events
2593
+ branches = @machine.transition(:parked => :ignite, :on => [:ignite, :shift_up])
2594
+
2595
+ assert_equal 2, branches.length
2596
+ assert_equal [:ignite, :shift_up], @machine.events.map {|event| event.name}
2597
+ end
2598
+
2599
+ def test_should_not_modify_options
2600
+ options = {:parked => :idling, :on => :ignite}
2601
+ @machine.transition(options)
2602
+
2603
+ assert_equal options, {:parked => :idling, :on => :ignite}
2604
+ end
2605
+ end
2606
+
2607
+ class MachineWithTransitionCallbacksTest < Test::Unit::TestCase
2608
+ def setup
2609
+ @klass = Class.new do
2610
+ attr_accessor :callbacks
2611
+ end
2612
+
2613
+ @machine = StateMachine::Machine.new(@klass, :initial => :parked)
2614
+ @event = @machine.event :ignite do
2615
+ transition :parked => :idling
2616
+ end
2617
+
2618
+ @object = @klass.new
2619
+ @object.callbacks = []
2620
+ end
2621
+
2622
+ def test_should_not_raise_exception_if_implicit_option_specified
2623
+ assert_nothing_raised {@machine.before_transition :invalid => :valid, :do => lambda {}}
2624
+ end
2625
+
2626
+ def test_should_raise_exception_if_method_not_specified
2627
+ exception = assert_raise(ArgumentError) {@machine.before_transition :to => :idling}
2628
+ assert_equal 'Method(s) for callback must be specified', exception.message
2629
+ end
2630
+
2631
+ def test_should_invoke_callbacks_during_transition
2632
+ @machine.before_transition lambda {|object| object.callbacks << 'before'}
2633
+ @machine.after_transition lambda {|object| object.callbacks << 'after'}
2634
+ @machine.around_transition lambda {|object, transition, block| object.callbacks << 'before_around'; block.call; object.callbacks << 'after_around'}
2635
+
2636
+ @event.fire(@object)
2637
+ assert_equal %w(before before_around after_around after), @object.callbacks
2638
+ end
2639
+
2640
+ def test_should_allow_multiple_callbacks
2641
+ @machine.before_transition lambda {|object| object.callbacks << 'before1'}, lambda {|object| object.callbacks << 'before2'}
2642
+ @machine.after_transition lambda {|object| object.callbacks << 'after1'}, lambda {|object| object.callbacks << 'after2'}
2643
+ @machine.around_transition(
2644
+ lambda {|object, transition, block| object.callbacks << 'before_around1'; block.call; object.callbacks << 'after_around1'},
2645
+ lambda {|object, transition, block| object.callbacks << 'before_around2'; block.call; object.callbacks << 'after_around2'}
2646
+ )
2647
+
2648
+ @event.fire(@object)
2649
+ assert_equal %w(before1 before2 before_around1 before_around2 after_around2 after_around1 after1 after2), @object.callbacks
2650
+ end
2651
+
2652
+ def test_should_allow_multiple_callbacks_with_requirements
2653
+ @machine.before_transition lambda {|object| object.callbacks << 'before_parked1'}, lambda {|object| object.callbacks << 'before_parked2'}, :from => :parked
2654
+ @machine.before_transition lambda {|object| object.callbacks << 'before_idling1'}, lambda {|object| object.callbacks << 'before_idling2'}, :from => :idling
2655
+ @machine.after_transition lambda {|object| object.callbacks << 'after_parked1'}, lambda {|object| object.callbacks << 'after_parked2'}, :from => :parked
2656
+ @machine.after_transition lambda {|object| object.callbacks << 'after_idling1'}, lambda {|object| object.callbacks << 'after_idling2'}, :from => :idling
2657
+ @machine.around_transition(
2658
+ lambda {|object, transition, block| object.callbacks << 'before_around_parked1'; block.call; object.callbacks << 'after_around_parked1'},
2659
+ lambda {|object, transition, block| object.callbacks << 'before_around_parked2'; block.call; object.callbacks << 'after_around_parked2'},
2660
+ :from => :parked
2661
+ )
2662
+ @machine.around_transition(
2663
+ lambda {|object, transition, block| object.callbacks << 'before_around_idling1'; block.call; object.callbacks << 'after_around_idling1'},
2664
+ lambda {|object, transition, block| object.callbacks << 'before_around_idling2'; block.call; object.callbacks << 'after_around_idling2'},
2665
+ :from => :idling
2666
+ )
2667
+
2668
+ @event.fire(@object)
2669
+ assert_equal %w(before_parked1 before_parked2 before_around_parked1 before_around_parked2 after_around_parked2 after_around_parked1 after_parked1 after_parked2), @object.callbacks
2670
+ end
2671
+
2672
+ def test_should_support_from_requirement
2673
+ @machine.before_transition :from => :parked, :do => lambda {|object| object.callbacks << :parked}
2674
+ @machine.before_transition :from => :idling, :do => lambda {|object| object.callbacks << :idling}
2675
+
2676
+ @event.fire(@object)
2677
+ assert_equal [:parked], @object.callbacks
2678
+ end
2679
+
2680
+ def test_should_support_except_from_requirement
2681
+ @machine.before_transition :except_from => :parked, :do => lambda {|object| object.callbacks << :parked}
2682
+ @machine.before_transition :except_from => :idling, :do => lambda {|object| object.callbacks << :idling}
2683
+
2684
+ @event.fire(@object)
2685
+ assert_equal [:idling], @object.callbacks
2686
+ end
2687
+
2688
+ def test_should_support_to_requirement
2689
+ @machine.before_transition :to => :parked, :do => lambda {|object| object.callbacks << :parked}
2690
+ @machine.before_transition :to => :idling, :do => lambda {|object| object.callbacks << :idling}
2691
+
2692
+ @event.fire(@object)
2693
+ assert_equal [:idling], @object.callbacks
2694
+ end
2695
+
2696
+ def test_should_support_except_to_requirement
2697
+ @machine.before_transition :except_to => :parked, :do => lambda {|object| object.callbacks << :parked}
2698
+ @machine.before_transition :except_to => :idling, :do => lambda {|object| object.callbacks << :idling}
2699
+
2700
+ @event.fire(@object)
2701
+ assert_equal [:parked], @object.callbacks
2702
+ end
2703
+
2704
+ def test_should_support_on_requirement
2705
+ @machine.before_transition :on => :park, :do => lambda {|object| object.callbacks << :park}
2706
+ @machine.before_transition :on => :ignite, :do => lambda {|object| object.callbacks << :ignite}
2707
+
2708
+ @event.fire(@object)
2709
+ assert_equal [:ignite], @object.callbacks
2710
+ end
2711
+
2712
+ def test_should_support_except_on_requirement
2713
+ @machine.before_transition :except_on => :park, :do => lambda {|object| object.callbacks << :park}
2714
+ @machine.before_transition :except_on => :ignite, :do => lambda {|object| object.callbacks << :ignite}
2715
+
2716
+ @event.fire(@object)
2717
+ assert_equal [:park], @object.callbacks
2718
+ end
2719
+
2720
+ def test_should_support_implicit_requirement
2721
+ @machine.before_transition :parked => :idling, :do => lambda {|object| object.callbacks << :parked}
2722
+ @machine.before_transition :idling => :parked, :do => lambda {|object| object.callbacks << :idling}
2723
+
2724
+ @event.fire(@object)
2725
+ assert_equal [:parked], @object.callbacks
2726
+ end
2727
+
2728
+ def test_should_track_states_defined_in_transition_callbacks
2729
+ @machine.before_transition :parked => :idling, :do => lambda {}
2730
+ @machine.after_transition :first_gear => :second_gear, :do => lambda {}
2731
+ @machine.around_transition :third_gear => :fourth_gear, :do => lambda {}
2732
+
2733
+ assert_equal [:parked, :idling, :first_gear, :second_gear, :third_gear, :fourth_gear], @machine.states.map {|state| state.name}
2734
+ end
2735
+
2736
+ def test_should_not_duplicate_states_defined_in_multiple_event_transitions
2737
+ @machine.before_transition :parked => :idling, :do => lambda {}
2738
+ @machine.after_transition :first_gear => :second_gear, :do => lambda {}
2739
+ @machine.after_transition :parked => :idling, :do => lambda {}
2740
+ @machine.around_transition :parked => :idling, :do => lambda {}
2741
+
2742
+ assert_equal [:parked, :idling, :first_gear, :second_gear], @machine.states.map {|state| state.name}
2743
+ end
2744
+
2745
+ def test_should_define_predicates_for_each_state
2746
+ [:parked?, :idling?].each {|predicate| assert @object.respond_to?(predicate)}
2747
+ end
2748
+ end
2749
+
2750
+ class MachineWithFailureCallbacksTest < Test::Unit::TestCase
2751
+ def setup
2752
+ @klass = Class.new do
2753
+ attr_accessor :callbacks
2754
+ end
2755
+
2756
+ @machine = StateMachine::Machine.new(@klass, :initial => :parked)
2757
+ @event = @machine.event :ignite
2758
+
2759
+ @object = @klass.new
2760
+ @object.callbacks = []
2761
+ end
2762
+
2763
+ def test_should_raise_exception_if_implicit_option_specified
2764
+ exception = assert_raise(ArgumentError) {@machine.after_failure :invalid => :valid, :do => lambda {}}
2765
+ assert_equal 'Invalid key(s): invalid', exception.message
2766
+ end
2767
+
2768
+ def test_should_raise_exception_if_method_not_specified
2769
+ exception = assert_raise(ArgumentError) {@machine.after_failure :on => :ignite}
2770
+ assert_equal 'Method(s) for callback must be specified', exception.message
2771
+ end
2772
+
2773
+ def test_should_invoke_callbacks_during_failed_transition
2774
+ @machine.after_failure lambda {|object| object.callbacks << 'failure'}
2775
+
2776
+ @event.fire(@object)
2777
+ assert_equal %w(failure), @object.callbacks
2778
+ end
2779
+
2780
+ def test_should_allow_multiple_callbacks
2781
+ @machine.after_failure lambda {|object| object.callbacks << 'failure1'}, lambda {|object| object.callbacks << 'failure2'}
2782
+
2783
+ @event.fire(@object)
2784
+ assert_equal %w(failure1 failure2), @object.callbacks
2785
+ end
2786
+
2787
+ def test_should_allow_multiple_callbacks_with_requirements
2788
+ @machine.after_failure lambda {|object| object.callbacks << 'failure_ignite1'}, lambda {|object| object.callbacks << 'failure_ignite2'}, :on => :ignite
2789
+ @machine.after_failure lambda {|object| object.callbacks << 'failure_park1'}, lambda {|object| object.callbacks << 'failure_park2'}, :on => :park
2790
+
2791
+ @event.fire(@object)
2792
+ assert_equal %w(failure_ignite1 failure_ignite2), @object.callbacks
2793
+ end
2794
+ end
2795
+
2796
+ class MachineWithPathsTest < Test::Unit::TestCase
2797
+ def setup
2798
+ @klass = Class.new
2799
+ @machine = StateMachine::Machine.new(@klass)
2800
+ @machine.event :ignite do
2801
+ transition :parked => :idling
2802
+ end
2803
+ @machine.event :shift_up do
2804
+ transition :first_gear => :second_gear
2805
+ end
2806
+
2807
+ @object = @klass.new
2808
+ @object.state = 'parked'
2809
+ end
2810
+
2811
+ def test_should_have_paths
2812
+ assert_equal [[StateMachine::Transition.new(@object, @machine, :ignite, :parked, :idling)]], @machine.paths_for(@object)
2813
+ end
2814
+
2815
+ def test_should_allow_requirement_configuration
2816
+ assert_equal [[StateMachine::Transition.new(@object, @machine, :shift_up, :first_gear, :second_gear)]], @machine.paths_for(@object, :from => :first_gear)
2817
+ end
2818
+ end
2819
+
2820
+ class MachineWithOwnerSubclassTest < Test::Unit::TestCase
2821
+ def setup
2822
+ @klass = Class.new
2823
+ @machine = StateMachine::Machine.new(@klass)
2824
+ @subclass = Class.new(@klass)
2825
+ end
2826
+
2827
+ def test_should_have_a_different_collection_of_state_machines
2828
+ assert_not_same @klass.state_machines, @subclass.state_machines
2829
+ end
2830
+
2831
+ def test_should_have_the_same_attribute_associated_state_machines
2832
+ assert_equal @klass.state_machines, @subclass.state_machines
2833
+ end
2834
+ end
2835
+
2836
+ class MachineWithExistingMachinesOnOwnerClassTest < Test::Unit::TestCase
2837
+ def setup
2838
+ @klass = Class.new
2839
+ @machine = StateMachine::Machine.new(@klass, :initial => :parked)
2840
+ @second_machine = StateMachine::Machine.new(@klass, :status, :initial => :idling)
2841
+ @object = @klass.new
2842
+ end
2843
+
2844
+ def test_should_track_each_state_machine
2845
+ expected = {:state => @machine, :status => @second_machine}
2846
+ assert_equal expected, @klass.state_machines
2847
+ end
2848
+
2849
+ def test_should_initialize_state_for_both_machines
2850
+ assert_equal 'parked', @object.state
2851
+ assert_equal 'idling', @object.status
2852
+ end
2853
+ end
2854
+
2855
+ class MachineWithExistingMachinesWithSameAttributesOnOwnerClassTest < Test::Unit::TestCase
2856
+ def setup
2857
+ @klass = Class.new
2858
+ @machine = StateMachine::Machine.new(@klass, :initial => :parked)
2859
+ @second_machine = StateMachine::Machine.new(@klass, :public_state, :initial => :idling, :attribute => :state)
2860
+ @object = @klass.new
2861
+ end
2862
+
2863
+ def test_should_track_each_state_machine
2864
+ expected = {:state => @machine, :public_state => @second_machine}
2865
+ assert_equal expected, @klass.state_machines
2866
+ end
2867
+
2868
+ def test_should_write_to_state_only_once
2869
+ @klass.class_eval do
2870
+ attr_reader :write_count
2871
+
2872
+ def state=(value)
2873
+ @write_count ||= 0
2874
+ @write_count += 1
2875
+ end
2876
+ end
2877
+ object = @klass.new
2878
+
2879
+ assert_equal 1, object.write_count
2880
+ end
2881
+
2882
+ def test_should_initialize_based_on_first_machine
2883
+ assert_equal 'parked', @object.state
2884
+ end
2885
+
2886
+ def test_should_not_allow_second_machine_to_initialize_state
2887
+ @object.state = nil
2888
+ @second_machine.initialize_state(@object)
2889
+ assert_nil @object.state
2890
+ end
2891
+
2892
+ def test_should_allow_transitions_on_both_machines
2893
+ @machine.event :ignite do
2894
+ transition :parked => :idling
2895
+ end
2896
+
2897
+ @second_machine.event :park do
2898
+ transition :idling => :parked
2899
+ end
2900
+
2901
+ @object.ignite
2902
+ assert_equal 'idling', @object.state
2903
+
2904
+ @object.park
2905
+ assert_equal 'parked', @object.state
2906
+ end
2907
+
2908
+ def test_should_copy_new_states_to_sibling_machines
2909
+ @first_gear = @machine.state :first_gear
2910
+ assert_equal @first_gear, @second_machine.state(:first_gear)
2911
+
2912
+ @second_gear = @second_machine.state :second_gear
2913
+ assert_equal @second_gear, @machine.state(:second_gear)
2914
+ end
2915
+
2916
+ def test_should_copy_all_existing_states_to_new_machines
2917
+ third_machine = StateMachine::Machine.new(@klass, :protected_state, :attribute => :state)
2918
+
2919
+ assert_equal @machine.state(:parked), third_machine.state(:parked)
2920
+ assert_equal @machine.state(:idling), third_machine.state(:idling)
2921
+ end
2922
+ end
2923
+
2924
+ class MachineWithExistingMachinesWithSameAttributesOnOwnerSubclassTest < Test::Unit::TestCase
2925
+ def setup
2926
+ @klass = Class.new
2927
+ @machine = StateMachine::Machine.new(@klass, :initial => :parked)
2928
+ @second_machine = StateMachine::Machine.new(@klass, :public_state, :initial => :idling, :attribute => :state)
2929
+
2930
+ @subclass = Class.new(@klass)
2931
+ @object = @subclass.new
2932
+ end
2933
+
2934
+ def test_should_not_copy_sibling_machines_to_subclass_after_initialization
2935
+ @subclass.state_machine(:state) {}
2936
+ assert_equal @klass.state_machine(:public_state), @subclass.state_machine(:public_state)
2937
+ end
2938
+
2939
+ def test_should_copy_sibling_machines_to_subclass_after_new_state
2940
+ subclass_machine = @subclass.state_machine(:state) {}
2941
+ subclass_machine.state :first_gear
2942
+ assert_not_equal @klass.state_machine(:public_state), @subclass.state_machine(:public_state)
2943
+ end
2944
+
2945
+ def test_should_copy_new_states_to_sibling_machines
2946
+ subclass_machine = @subclass.state_machine(:state) {}
2947
+ @first_gear = subclass_machine.state :first_gear
2948
+
2949
+ second_subclass_machine = @subclass.state_machine(:public_state)
2950
+ assert_equal @first_gear, second_subclass_machine.state(:first_gear)
2951
+ end
2952
+ end
2953
+
2954
+ class MachineWithNamespaceTest < Test::Unit::TestCase
2955
+ def setup
2956
+ @klass = Class.new
2957
+ @machine = StateMachine::Machine.new(@klass, :namespace => 'alarm', :initial => :active) do
2958
+ event :enable do
2959
+ transition :off => :active
2960
+ end
2961
+
2962
+ event :disable do
2963
+ transition :active => :off
2964
+ end
2965
+ end
2966
+ @object = @klass.new
2967
+ end
2968
+
2969
+ def test_should_namespace_state_predicates
2970
+ [:alarm_active?, :alarm_off?].each do |name|
2971
+ assert @object.respond_to?(name)
2972
+ end
2973
+ end
2974
+
2975
+ def test_should_namespace_event_checks
2976
+ [:can_enable_alarm?, :can_disable_alarm?].each do |name|
2977
+ assert @object.respond_to?(name)
2978
+ end
2979
+ end
2980
+
2981
+ def test_should_namespace_event_transition_readers
2982
+ [:enable_alarm_transition, :disable_alarm_transition].each do |name|
2983
+ assert @object.respond_to?(name)
2984
+ end
2985
+ end
2986
+
2987
+ def test_should_namespace_events
2988
+ [:enable_alarm, :disable_alarm].each do |name|
2989
+ assert @object.respond_to?(name)
2990
+ end
2991
+ end
2992
+
2993
+ def test_should_namespace_bang_events
2994
+ [:enable_alarm!, :disable_alarm!].each do |name|
2995
+ assert @object.respond_to?(name)
2996
+ end
2997
+ end
2998
+ end
2999
+
3000
+ class MachineWithCustomAttributeTest < Test::Unit::TestCase
3001
+ def setup
3002
+ StateMachine::Integrations.const_set('Custom', Module.new do
3003
+ include StateMachine::Integrations::Base
3004
+
3005
+ @defaults = {:action => :save, :use_transactions => false}
3006
+
3007
+ def create_with_scope(name)
3008
+ lambda {}
3009
+ end
3010
+
3011
+ def create_without_scope(name)
3012
+ lambda {}
3013
+ end
3014
+ end)
3015
+
3016
+ @klass = Class.new
3017
+ @machine = StateMachine::Machine.new(@klass, :state, :attribute => :state_id, :initial => :active, :integration => :custom) do
3018
+ event :ignite do
3019
+ transition :parked => :idling
3020
+ end
3021
+ end
3022
+ @object = @klass.new
3023
+ end
3024
+
3025
+ def test_should_define_a_reader_attribute_for_the_attribute
3026
+ assert @object.respond_to?(:state_id)
3027
+ end
3028
+
3029
+ def test_should_define_a_writer_attribute_for_the_attribute
3030
+ assert @object.respond_to?(:state_id=)
3031
+ end
3032
+
3033
+ def test_should_define_a_predicate_for_the_attribute
3034
+ assert @object.respond_to?(:state?)
3035
+ end
3036
+
3037
+ def test_should_define_a_name_reader_for_the_attribute
3038
+ assert @object.respond_to?(:state_name)
3039
+ end
3040
+
3041
+ def test_should_define_a_human_name_reader_for_the_attribute
3042
+ assert @object.respond_to?(:state_name)
3043
+ end
3044
+
3045
+ def test_should_define_an_event_reader_for_the_attribute
3046
+ assert @object.respond_to?(:state_events)
3047
+ end
3048
+
3049
+ def test_should_define_a_transition_reader_for_the_attribute
3050
+ assert @object.respond_to?(:state_transitions)
3051
+ end
3052
+
3053
+ def test_should_define_a_path_reader_for_the_attribute
3054
+ assert @object.respond_to?(:state_paths)
3055
+ end
3056
+
3057
+ def test_should_define_an_event_runner_for_the_attribute
3058
+ assert @object.respond_to?(:fire_state_event)
3059
+ end
3060
+
3061
+ def test_should_define_a_human_attribute_name_reader
3062
+ assert @klass.respond_to?(:human_state_name)
3063
+ end
3064
+
3065
+ def test_should_define_a_human_event_name_reader
3066
+ assert @klass.respond_to?(:human_state_event_name)
3067
+ end
3068
+
3069
+ def test_should_define_singular_with_scope
3070
+ assert @klass.respond_to?(:with_state)
3071
+ end
3072
+
3073
+ def test_should_define_singular_without_scope
3074
+ assert @klass.respond_to?(:without_state)
3075
+ end
3076
+
3077
+ def test_should_define_plural_with_scope
3078
+ assert @klass.respond_to?(:with_states)
3079
+ end
3080
+
3081
+ def test_should_define_plural_without_scope
3082
+ assert @klass.respond_to?(:without_states)
3083
+ end
3084
+
3085
+ def test_should_define_state_machines_reader
3086
+ expected = {:state => @machine}
3087
+ assert_equal expected, @klass.state_machines
3088
+ end
3089
+
3090
+ def teardown
3091
+ StateMachine::Integrations.send(:remove_const, 'Custom')
3092
+ end
3093
+ end
3094
+
3095
+ class MachineFinderWithoutExistingMachineTest < Test::Unit::TestCase
3096
+ def setup
3097
+ @klass = Class.new
3098
+ @machine = StateMachine::Machine.find_or_create(@klass)
3099
+ end
3100
+
3101
+ def test_should_accept_a_block
3102
+ called = false
3103
+ StateMachine::Machine.find_or_create(Class.new) do
3104
+ called = respond_to?(:event)
3105
+ end
3106
+
3107
+ assert called
3108
+ end
3109
+
3110
+ def test_should_create_a_new_machine
3111
+ assert_not_nil @machine
3112
+ end
3113
+
3114
+ def test_should_use_default_state
3115
+ assert_equal :state, @machine.attribute
3116
+ end
3117
+ end
3118
+
3119
+ class MachineFinderWithExistingOnSameClassTest < Test::Unit::TestCase
3120
+ def setup
3121
+ @klass = Class.new
3122
+ @existing_machine = StateMachine::Machine.new(@klass)
3123
+ @machine = StateMachine::Machine.find_or_create(@klass)
3124
+ end
3125
+
3126
+ def test_should_accept_a_block
3127
+ called = false
3128
+ StateMachine::Machine.find_or_create(@klass) do
3129
+ called = respond_to?(:event)
3130
+ end
3131
+
3132
+ assert called
3133
+ end
3134
+
3135
+ def test_should_not_create_a_new_machine
3136
+ assert_same @machine, @existing_machine
3137
+ end
3138
+ end
3139
+
3140
+ class MachineFinderWithExistingMachineOnSuperclassTest < Test::Unit::TestCase
3141
+ def setup
3142
+ integration = Module.new do
3143
+ include StateMachine::Integrations::Base
3144
+
3145
+ def self.matches?(klass)
3146
+ false
3147
+ end
3148
+ end
3149
+ StateMachine::Integrations.const_set('Custom', integration)
3150
+
3151
+ @base_class = Class.new
3152
+ @base_machine = StateMachine::Machine.new(@base_class, :status, :action => :save, :integration => :custom)
3153
+ @base_machine.event(:ignite) {}
3154
+ @base_machine.before_transition(lambda {})
3155
+ @base_machine.after_transition(lambda {})
3156
+ @base_machine.around_transition(lambda {})
3157
+
3158
+ @klass = Class.new(@base_class)
3159
+ @machine = StateMachine::Machine.find_or_create(@klass, :status) {}
3160
+ end
3161
+
3162
+ def test_should_accept_a_block
3163
+ called = false
3164
+ StateMachine::Machine.find_or_create(Class.new(@base_class)) do
3165
+ called = respond_to?(:event)
3166
+ end
3167
+
3168
+ assert called
3169
+ end
3170
+
3171
+ def test_should_not_create_a_new_machine_if_no_block_or_options
3172
+ machine = StateMachine::Machine.find_or_create(Class.new(@base_class), :status)
3173
+
3174
+ assert_same machine, @base_machine
3175
+ end
3176
+
3177
+ def test_should_create_a_new_machine_if_given_options
3178
+ machine = StateMachine::Machine.find_or_create(@klass, :status, :initial => :parked)
3179
+
3180
+ assert_not_nil machine
3181
+ assert_not_same machine, @base_machine
3182
+ end
3183
+
3184
+ def test_should_create_a_new_machine_if_given_block
3185
+ assert_not_nil @machine
3186
+ assert_not_same @machine, @base_machine
3187
+ end
3188
+
3189
+ def test_should_copy_the_base_attribute
3190
+ assert_equal :status, @machine.attribute
3191
+ end
3192
+
3193
+ def test_should_copy_the_base_configuration
3194
+ assert_equal :save, @machine.action
3195
+ end
3196
+
3197
+ def test_should_copy_events
3198
+ # Can't assert equal arrays since their machines change
3199
+ assert_equal 1, @machine.events.length
3200
+ end
3201
+
3202
+ def test_should_copy_before_callbacks
3203
+ assert_equal @base_machine.callbacks[:before], @machine.callbacks[:before]
3204
+ end
3205
+
3206
+ def test_should_copy_after_transitions
3207
+ assert_equal @base_machine.callbacks[:after], @machine.callbacks[:after]
3208
+ end
3209
+
3210
+ def test_should_use_the_same_integration
3211
+ assert((class << @machine; ancestors; end).include?(StateMachine::Integrations::Custom))
3212
+ end
3213
+
3214
+ def teardown
3215
+ StateMachine::Integrations.send(:remove_const, 'Custom')
3216
+ end
3217
+ end
3218
+
3219
+ class MachineFinderCustomOptionsTest < Test::Unit::TestCase
3220
+ def setup
3221
+ @klass = Class.new
3222
+ @machine = StateMachine::Machine.find_or_create(@klass, :status, :initial => :parked)
3223
+ @object = @klass.new
3224
+ end
3225
+
3226
+ def test_should_use_custom_attribute
3227
+ assert_equal :status, @machine.attribute
3228
+ end
3229
+
3230
+ def test_should_set_custom_initial_state
3231
+ assert_equal :parked, @machine.initial_state(@object).name
3232
+ end
3233
+ end
3234
+
3235
+ begin
3236
+ # Load library
3237
+ require 'graphviz'
3238
+
3239
+ class MachineDrawingTest < Test::Unit::TestCase
3240
+ def setup
3241
+ @klass = Class.new do
3242
+ def self.name; @name ||= "Vehicle_#{rand(1000000)}"; end
3243
+ end
3244
+ @machine = StateMachine::Machine.new(@klass, :initial => :parked)
3245
+ @machine.event :ignite do
3246
+ transition :parked => :idling
3247
+ end
3248
+ end
3249
+
3250
+ def test_should_raise_exception_if_invalid_option_specified
3251
+ assert_raise(ArgumentError) {@machine.draw(:invalid => true)}
3252
+ end
3253
+
3254
+ def test_should_save_file_with_class_name_by_default
3255
+ @machine.draw
3256
+ assert File.exists?("./#{@klass.name}_state.png")
3257
+ end
3258
+
3259
+ def test_should_allow_base_name_to_be_customized
3260
+ name = "machine_#{rand(1000000)}"
3261
+ @machine.draw(:name => name)
3262
+ @path = "./#{name}.png"
3263
+ assert File.exists?(@path)
3264
+ end
3265
+
3266
+ def test_should_allow_format_to_be_customized
3267
+ @machine.draw(:format => 'jpg')
3268
+ @path = "./#{@klass.name}_state.jpg"
3269
+ assert File.exists?(@path)
3270
+ end
3271
+
3272
+ def test_should_allow_path_to_be_customized
3273
+ @machine.draw(:path => "#{File.dirname(__FILE__)}/")
3274
+ @path = "#{File.dirname(__FILE__)}/#{@klass.name}_state.png"
3275
+ assert File.exists?(@path)
3276
+ end
3277
+
3278
+ def test_should_allow_orientation_to_be_landscape
3279
+ graph = @machine.draw(:orientation => 'landscape')
3280
+ assert_equal 'LR', graph['rankdir'].to_s.gsub('"', '')
3281
+ end
3282
+
3283
+ def test_should_allow_orientation_to_be_portrait
3284
+ graph = @machine.draw(:orientation => 'portrait')
3285
+ assert_equal 'TB', graph['rankdir'].to_s.gsub('"', '')
3286
+ end
3287
+
3288
+ if Constants::RGV_VERSION != '0.9.0'
3289
+ def test_should_allow_human_names_to_be_displayed
3290
+ @machine.event :ignite, :human_name => 'Ignite'
3291
+ @machine.state :parked, :human_name => 'Parked'
3292
+ @machine.state :idling, :human_name => 'Idling'
3293
+ graph = @machine.draw(:human_names => true)
3294
+
3295
+ parked_node = graph.get_node('parked')
3296
+ assert_equal 'Parked', parked_node['label'].to_s.gsub('"', '')
3297
+
3298
+ idling_node = graph.get_node('idling')
3299
+ assert_equal 'Idling', idling_node['label'].to_s.gsub('"', '')
3300
+ end
3301
+ end
3302
+
3303
+ def teardown
3304
+ FileUtils.rm Dir[@path || "./#{@klass.name}_state.png"]
3305
+ end
3306
+ end
3307
+
3308
+ class MachineDrawingWithIntegerStatesTest < Test::Unit::TestCase
3309
+ def setup
3310
+ @klass = Class.new do
3311
+ def self.name; @name ||= "Vehicle_#{rand(1000000)}"; end
3312
+ end
3313
+ @machine = StateMachine::Machine.new(@klass, :state_id, :initial => :parked)
3314
+ @machine.event :ignite do
3315
+ transition :parked => :idling
3316
+ end
3317
+ @machine.state :parked, :value => 1
3318
+ @machine.state :idling, :value => 2
3319
+ @graph = @machine.draw
3320
+ end
3321
+
3322
+ def test_should_draw_all_states
3323
+ assert_equal 3, @graph.node_count
3324
+ end
3325
+
3326
+ def test_should_draw_all_events
3327
+ assert_equal 2, @graph.edge_count
3328
+ end
3329
+
3330
+ def test_should_draw_machine
3331
+ assert File.exist?("./#{@klass.name}_state_id.png")
3332
+ end
3333
+
3334
+ def teardown
3335
+ FileUtils.rm Dir["./#{@klass.name}_state_id.png"]
3336
+ end
3337
+ end
3338
+
3339
+ class MachineDrawingWithNilStatesTest < Test::Unit::TestCase
3340
+ def setup
3341
+ @klass = Class.new do
3342
+ def self.name; @name ||= "Vehicle_#{rand(1000000)}"; end
3343
+ end
3344
+ @machine = StateMachine::Machine.new(@klass, :initial => :parked)
3345
+ @machine.event :ignite do
3346
+ transition :parked => :idling
3347
+ end
3348
+ @machine.state :parked, :value => nil
3349
+ @graph = @machine.draw
3350
+ end
3351
+
3352
+ def test_should_draw_all_states
3353
+ assert_equal 3, @graph.node_count
3354
+ end
3355
+
3356
+ def test_should_draw_all_events
3357
+ assert_equal 2, @graph.edge_count
3358
+ end
3359
+
3360
+ def test_should_draw_machine
3361
+ assert File.exist?("./#{@klass.name}_state.png")
3362
+ end
3363
+
3364
+ def teardown
3365
+ FileUtils.rm Dir["./#{@klass.name}_state.png"]
3366
+ end
3367
+ end
3368
+
3369
+ class MachineDrawingWithDynamicStatesTest < Test::Unit::TestCase
3370
+ def setup
3371
+ @klass = Class.new do
3372
+ def self.name; @name ||= "Vehicle_#{rand(1000000)}"; end
3373
+ end
3374
+ @machine = StateMachine::Machine.new(@klass, :initial => :parked)
3375
+ @machine.event :activate do
3376
+ transition :parked => :idling
3377
+ end
3378
+ @machine.state :idling, :value => lambda {Time.now}
3379
+ @graph = @machine.draw
3380
+ end
3381
+
3382
+ def test_should_draw_all_states
3383
+ assert_equal 3, @graph.node_count
3384
+ end
3385
+
3386
+ def test_should_draw_all_events
3387
+ assert_equal 2, @graph.edge_count
3388
+ end
3389
+
3390
+ def test_should_draw_machine
3391
+ assert File.exist?("./#{@klass.name}_state.png")
3392
+ end
3393
+
3394
+ def teardown
3395
+ FileUtils.rm Dir["./#{@klass.name}_state.png"]
3396
+ end
3397
+ end
3398
+
3399
+ class MachineClassDrawingTest < Test::Unit::TestCase
3400
+ def setup
3401
+ @klass = Class.new do
3402
+ def self.name; @name ||= "Vehicle_#{rand(1000000)}"; end
3403
+ end
3404
+ @machine = StateMachine::Machine.new(@klass)
3405
+ @machine.event :ignite do
3406
+ transition :parked => :idling
3407
+ end
3408
+ end
3409
+
3410
+ def test_should_raise_exception_if_no_class_names_specified
3411
+ exception = assert_raise(ArgumentError) {StateMachine::Machine.draw(nil)}
3412
+ assert_equal 'At least one class must be specified', exception.message
3413
+ end
3414
+
3415
+ def test_should_load_files
3416
+ StateMachine::Machine.draw('Switch', :file => File.expand_path("#{File.dirname(__FILE__)}/../files/switch.rb"))
3417
+ assert defined?(::Switch)
3418
+ end
3419
+
3420
+ def test_should_allow_path_and_format_to_be_customized
3421
+ StateMachine::Machine.draw('Switch', :file => File.expand_path("#{File.dirname(__FILE__)}/../files/switch.rb"), :path => "#{File.dirname(__FILE__)}/", :format => 'jpg')
3422
+ assert File.exist?("#{File.dirname(__FILE__)}/#{Switch.name}_state.jpg")
3423
+ end
3424
+
3425
+ def teardown
3426
+ FileUtils.rm Dir["{.,#{File.dirname(__FILE__)}}/#{Switch.name}_state.{jpg,png}"]
3427
+ end
3428
+ end
3429
+ rescue LoadError
3430
+ $stderr.puts 'Skipping GraphViz StateMachine::Machine tests. `gem install ruby-graphviz` >= v0.9.17 and try again.'
3431
+ end unless ENV['TRAVIS']