cm-state_machine 1.2.0.1

Sign up to get free protection for your applications and to get access to all the features.
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 +1260 -0
  10. data/Rakefile +41 -0
  11. data/cm-state_machine.gemspec +22 -0
  12. data/examples/AutoShop_state.png +0 -0
  13. data/examples/Car_state.png +0 -0
  14. data/examples/Gemfile +5 -0
  15. data/examples/Gemfile.lock +14 -0
  16. data/examples/TrafficLight_state.png +0 -0
  17. data/examples/Vehicle_state.png +0 -0
  18. data/examples/auto_shop.rb +13 -0
  19. data/examples/car.rb +21 -0
  20. data/examples/doc/AutoShop.html +2856 -0
  21. data/examples/doc/AutoShop_state.png +0 -0
  22. data/examples/doc/Car.html +919 -0
  23. data/examples/doc/Car_state.png +0 -0
  24. data/examples/doc/TrafficLight.html +2230 -0
  25. data/examples/doc/TrafficLight_state.png +0 -0
  26. data/examples/doc/Vehicle.html +7921 -0
  27. data/examples/doc/Vehicle_state.png +0 -0
  28. data/examples/doc/_index.html +136 -0
  29. data/examples/doc/class_list.html +47 -0
  30. data/examples/doc/css/common.css +1 -0
  31. data/examples/doc/css/full_list.css +55 -0
  32. data/examples/doc/css/style.css +322 -0
  33. data/examples/doc/file_list.html +46 -0
  34. data/examples/doc/frames.html +13 -0
  35. data/examples/doc/index.html +136 -0
  36. data/examples/doc/js/app.js +205 -0
  37. data/examples/doc/js/full_list.js +173 -0
  38. data/examples/doc/js/jquery.js +16 -0
  39. data/examples/doc/method_list.html +734 -0
  40. data/examples/doc/top-level-namespace.html +105 -0
  41. data/examples/merb-rest/controller.rb +51 -0
  42. data/examples/merb-rest/model.rb +28 -0
  43. data/examples/merb-rest/view_edit.html.erb +24 -0
  44. data/examples/merb-rest/view_index.html.erb +23 -0
  45. data/examples/merb-rest/view_new.html.erb +13 -0
  46. data/examples/merb-rest/view_show.html.erb +17 -0
  47. data/examples/rails-rest/controller.rb +43 -0
  48. data/examples/rails-rest/migration.rb +7 -0
  49. data/examples/rails-rest/model.rb +23 -0
  50. data/examples/rails-rest/view__form.html.erb +34 -0
  51. data/examples/rails-rest/view_edit.html.erb +6 -0
  52. data/examples/rails-rest/view_index.html.erb +25 -0
  53. data/examples/rails-rest/view_new.html.erb +5 -0
  54. data/examples/rails-rest/view_show.html.erb +19 -0
  55. data/examples/traffic_light.rb +9 -0
  56. data/examples/vehicle.rb +33 -0
  57. data/gemfiles/active_model_3.0.0.gemfile +7 -0
  58. data/gemfiles/active_model_3.0.0.gemfile.lock +35 -0
  59. data/gemfiles/active_model_3.0.5.gemfile +7 -0
  60. data/gemfiles/active_model_3.0.5.gemfile.lock +35 -0
  61. data/gemfiles/active_model_3.1.1.gemfile +7 -0
  62. data/gemfiles/active_model_3.1.1.gemfile.lock +36 -0
  63. data/gemfiles/active_model_3.2.1.gemfile +7 -0
  64. data/gemfiles/active_model_3.2.12.gemfile +7 -0
  65. data/gemfiles/active_model_3.2.12.gemfile.lock +36 -0
  66. data/gemfiles/active_model_3.2.13.rc1.gemfile +7 -0
  67. data/gemfiles/active_model_3.2.13.rc1.gemfile.lock +36 -0
  68. data/gemfiles/active_model_4.0.0.gemfile +9 -0
  69. data/gemfiles/active_model_4.0.0.gemfile.lock +78 -0
  70. data/gemfiles/active_record_2.0.0.gemfile +9 -0
  71. data/gemfiles/active_record_2.0.0.gemfile.lock +39 -0
  72. data/gemfiles/active_record_2.0.5.gemfile +9 -0
  73. data/gemfiles/active_record_2.0.5.gemfile.lock +39 -0
  74. data/gemfiles/active_record_2.1.0.gemfile +9 -0
  75. data/gemfiles/active_record_2.1.0.gemfile.lock +39 -0
  76. data/gemfiles/active_record_2.1.2.gemfile +9 -0
  77. data/gemfiles/active_record_2.1.2.gemfile.lock +39 -0
  78. data/gemfiles/active_record_2.2.3.gemfile +9 -0
  79. data/gemfiles/active_record_2.2.3.gemfile.lock +39 -0
  80. data/gemfiles/active_record_2.3.12.gemfile +9 -0
  81. data/gemfiles/active_record_2.3.12.gemfile.lock +39 -0
  82. data/gemfiles/active_record_2.3.5.gemfile +9 -0
  83. data/gemfiles/active_record_2.3.5.gemfile.lock +39 -0
  84. data/gemfiles/active_record_3.0.0.gemfile +9 -0
  85. data/gemfiles/active_record_3.0.0.gemfile.lock +51 -0
  86. data/gemfiles/active_record_3.0.5.gemfile +9 -0
  87. data/gemfiles/active_record_3.0.5.gemfile.lock +50 -0
  88. data/gemfiles/active_record_3.1.1.gemfile +9 -0
  89. data/gemfiles/active_record_3.1.1.gemfile.lock +51 -0
  90. data/gemfiles/active_record_3.2.12.gemfile +9 -0
  91. data/gemfiles/active_record_3.2.12.gemfile.lock +51 -0
  92. data/gemfiles/active_record_3.2.13.rc1.gemfile +9 -0
  93. data/gemfiles/active_record_3.2.13.rc1.gemfile.lock +51 -0
  94. data/gemfiles/active_record_4.0.0.gemfile +11 -0
  95. data/gemfiles/active_record_4.0.0.gemfile.lock +83 -0
  96. data/gemfiles/data_mapper_0.10.2.gemfile +13 -0
  97. data/gemfiles/data_mapper_0.10.2.gemfile.lock +56 -0
  98. data/gemfiles/data_mapper_0.9.11.gemfile +13 -0
  99. data/gemfiles/data_mapper_0.9.11.gemfile.lock +71 -0
  100. data/gemfiles/data_mapper_0.9.4.gemfile +12 -0
  101. data/gemfiles/data_mapper_0.9.4.gemfile.lock +70 -0
  102. data/gemfiles/data_mapper_0.9.7.gemfile +13 -0
  103. data/gemfiles/data_mapper_0.9.7.gemfile.lock +67 -0
  104. data/gemfiles/data_mapper_1.0.0.gemfile +12 -0
  105. data/gemfiles/data_mapper_1.0.0.gemfile.lock +63 -0
  106. data/gemfiles/data_mapper_1.0.1.gemfile +12 -0
  107. data/gemfiles/data_mapper_1.0.1.gemfile.lock +63 -0
  108. data/gemfiles/data_mapper_1.0.2.gemfile +12 -0
  109. data/gemfiles/data_mapper_1.0.2.gemfile.lock +63 -0
  110. data/gemfiles/data_mapper_1.1.0.gemfile +12 -0
  111. data/gemfiles/data_mapper_1.1.0.gemfile.lock +61 -0
  112. data/gemfiles/data_mapper_1.2.0.gemfile +12 -0
  113. data/gemfiles/data_mapper_1.2.0.gemfile.lock +61 -0
  114. data/gemfiles/default.gemfile +7 -0
  115. data/gemfiles/default.gemfile.lock +27 -0
  116. data/gemfiles/graphviz_0.9.17.gemfile +7 -0
  117. data/gemfiles/graphviz_0.9.17.gemfile.lock +29 -0
  118. data/gemfiles/graphviz_0.9.21.gemfile +7 -0
  119. data/gemfiles/graphviz_0.9.21.gemfile.lock +29 -0
  120. data/gemfiles/graphviz_1.0.0.gemfile +7 -0
  121. data/gemfiles/graphviz_1.0.0.gemfile.lock +29 -0
  122. data/gemfiles/graphviz_1.0.3.gemfile +7 -0
  123. data/gemfiles/graphviz_1.0.3.gemfile.lock +29 -0
  124. data/gemfiles/graphviz_1.0.8.gemfile +7 -0
  125. data/gemfiles/graphviz_1.0.8.gemfile.lock +29 -0
  126. data/gemfiles/mongo_mapper_0.10.0.gemfile +8 -0
  127. data/gemfiles/mongo_mapper_0.10.0.gemfile.lock +47 -0
  128. data/gemfiles/mongo_mapper_0.11.2.gemfile +9 -0
  129. data/gemfiles/mongo_mapper_0.11.2.gemfile.lock +48 -0
  130. data/gemfiles/mongo_mapper_0.12.0.gemfile +9 -0
  131. data/gemfiles/mongo_mapper_0.12.0.gemfile.lock +48 -0
  132. data/gemfiles/mongo_mapper_0.5.5.gemfile +8 -0
  133. data/gemfiles/mongo_mapper_0.5.5.gemfile.lock +36 -0
  134. data/gemfiles/mongo_mapper_0.5.8.gemfile +8 -0
  135. data/gemfiles/mongo_mapper_0.5.8.gemfile.lock +36 -0
  136. data/gemfiles/mongo_mapper_0.6.0.gemfile +8 -0
  137. data/gemfiles/mongo_mapper_0.6.0.gemfile.lock +36 -0
  138. data/gemfiles/mongo_mapper_0.6.10.gemfile +8 -0
  139. data/gemfiles/mongo_mapper_0.6.10.gemfile.lock +36 -0
  140. data/gemfiles/mongo_mapper_0.7.0.gemfile +8 -0
  141. data/gemfiles/mongo_mapper_0.7.0.gemfile.lock +36 -0
  142. data/gemfiles/mongo_mapper_0.7.5.gemfile +8 -0
  143. data/gemfiles/mongo_mapper_0.7.5.gemfile.lock +39 -0
  144. data/gemfiles/mongo_mapper_0.8.0.gemfile +10 -0
  145. data/gemfiles/mongo_mapper_0.8.0.gemfile.lock +43 -0
  146. data/gemfiles/mongo_mapper_0.8.3.gemfile +10 -0
  147. data/gemfiles/mongo_mapper_0.8.3.gemfile.lock +43 -0
  148. data/gemfiles/mongo_mapper_0.8.4.gemfile +8 -0
  149. data/gemfiles/mongo_mapper_0.8.4.gemfile.lock +42 -0
  150. data/gemfiles/mongo_mapper_0.8.6.gemfile +8 -0
  151. data/gemfiles/mongo_mapper_0.8.6.gemfile.lock +42 -0
  152. data/gemfiles/mongo_mapper_0.9.0.gemfile +7 -0
  153. data/gemfiles/mongo_mapper_0.9.0.gemfile.lock +45 -0
  154. data/gemfiles/mongoid_2.0.0.gemfile +9 -0
  155. data/gemfiles/mongoid_2.0.0.gemfile.lock +49 -0
  156. data/gemfiles/mongoid_2.1.4.gemfile +9 -0
  157. data/gemfiles/mongoid_2.1.4.gemfile.lock +47 -0
  158. data/gemfiles/mongoid_2.2.4.gemfile +9 -0
  159. data/gemfiles/mongoid_2.2.4.gemfile.lock +47 -0
  160. data/gemfiles/mongoid_2.3.3.gemfile +9 -0
  161. data/gemfiles/mongoid_2.3.3.gemfile.lock +47 -0
  162. data/gemfiles/mongoid_2.4.0.gemfile +9 -0
  163. data/gemfiles/mongoid_2.4.0.gemfile.lock +47 -0
  164. data/gemfiles/mongoid_2.4.10.gemfile +9 -0
  165. data/gemfiles/mongoid_2.4.10.gemfile.lock +47 -0
  166. data/gemfiles/mongoid_2.5.2.gemfile +9 -0
  167. data/gemfiles/mongoid_2.5.2.gemfile.lock +47 -0
  168. data/gemfiles/mongoid_2.6.0.gemfile +9 -0
  169. data/gemfiles/mongoid_2.6.0.gemfile.lock +47 -0
  170. data/gemfiles/mongoid_3.0.0.gemfile +8 -0
  171. data/gemfiles/mongoid_3.0.0.gemfile.lock +45 -0
  172. data/gemfiles/mongoid_3.0.22.gemfile +8 -0
  173. data/gemfiles/mongoid_3.0.22.gemfile.lock +45 -0
  174. data/gemfiles/mongoid_3.1.0.gemfile +8 -0
  175. data/gemfiles/mongoid_3.1.0.gemfile.lock +45 -0
  176. data/gemfiles/sequel_2.11.0.gemfile +9 -0
  177. data/gemfiles/sequel_2.11.0.gemfile.lock +33 -0
  178. data/gemfiles/sequel_2.12.0.gemfile +9 -0
  179. data/gemfiles/sequel_2.12.0.gemfile.lock +33 -0
  180. data/gemfiles/sequel_2.8.0.gemfile +9 -0
  181. data/gemfiles/sequel_2.8.0.gemfile.lock +33 -0
  182. data/gemfiles/sequel_3.0.0.gemfile +9 -0
  183. data/gemfiles/sequel_3.0.0.gemfile.lock +33 -0
  184. data/gemfiles/sequel_3.10.0.gemfile +9 -0
  185. data/gemfiles/sequel_3.10.0.gemfile.lock +33 -0
  186. data/gemfiles/sequel_3.13.0.gemfile +9 -0
  187. data/gemfiles/sequel_3.13.0.gemfile.lock +33 -0
  188. data/gemfiles/sequel_3.14.0.gemfile +9 -0
  189. data/gemfiles/sequel_3.14.0.gemfile.lock +33 -0
  190. data/gemfiles/sequel_3.23.0.gemfile +9 -0
  191. data/gemfiles/sequel_3.23.0.gemfile.lock +33 -0
  192. data/gemfiles/sequel_3.24.0.gemfile +9 -0
  193. data/gemfiles/sequel_3.24.0.gemfile.lock +33 -0
  194. data/gemfiles/sequel_3.29.0.gemfile +9 -0
  195. data/gemfiles/sequel_3.29.0.gemfile.lock +33 -0
  196. data/gemfiles/sequel_3.34.0.gemfile +9 -0
  197. data/gemfiles/sequel_3.34.0.gemfile.lock +33 -0
  198. data/gemfiles/sequel_3.35.0.gemfile +9 -0
  199. data/gemfiles/sequel_3.35.0.gemfile.lock +33 -0
  200. data/gemfiles/sequel_3.4.0.gemfile +9 -0
  201. data/gemfiles/sequel_3.4.0.gemfile.lock +33 -0
  202. data/gemfiles/sequel_3.44.0.gemfile +9 -0
  203. data/gemfiles/sequel_3.44.0.gemfile.lock +33 -0
  204. data/init.rb +1 -0
  205. data/lib/state_machine.rb +8 -0
  206. data/lib/state_machine/assertions.rb +36 -0
  207. data/lib/state_machine/branch.rb +225 -0
  208. data/lib/state_machine/callback.rb +236 -0
  209. data/lib/state_machine/core.rb +12 -0
  210. data/lib/state_machine/core_ext.rb +2 -0
  211. data/lib/state_machine/core_ext/class/state_machine.rb +5 -0
  212. data/lib/state_machine/error.rb +13 -0
  213. data/lib/state_machine/eval_helpers.rb +87 -0
  214. data/lib/state_machine/event.rb +257 -0
  215. data/lib/state_machine/event_collection.rb +141 -0
  216. data/lib/state_machine/extensions.rb +149 -0
  217. data/lib/state_machine/graph.rb +92 -0
  218. data/lib/state_machine/helper_module.rb +17 -0
  219. data/lib/state_machine/initializers.rb +4 -0
  220. data/lib/state_machine/initializers/merb.rb +1 -0
  221. data/lib/state_machine/initializers/rails.rb +25 -0
  222. data/lib/state_machine/integrations.rb +121 -0
  223. data/lib/state_machine/integrations/active_model.rb +585 -0
  224. data/lib/state_machine/integrations/active_model/locale.rb +11 -0
  225. data/lib/state_machine/integrations/active_model/observer.rb +33 -0
  226. data/lib/state_machine/integrations/active_model/observer_update.rb +42 -0
  227. data/lib/state_machine/integrations/active_model/versions.rb +31 -0
  228. data/lib/state_machine/integrations/active_record.rb +552 -0
  229. data/lib/state_machine/integrations/active_record/locale.rb +20 -0
  230. data/lib/state_machine/integrations/active_record/versions.rb +123 -0
  231. data/lib/state_machine/integrations/base.rb +100 -0
  232. data/lib/state_machine/integrations/data_mapper.rb +511 -0
  233. data/lib/state_machine/integrations/data_mapper/observer.rb +210 -0
  234. data/lib/state_machine/integrations/data_mapper/versions.rb +85 -0
  235. data/lib/state_machine/integrations/mongo_mapper.rb +389 -0
  236. data/lib/state_machine/integrations/mongo_mapper/locale.rb +4 -0
  237. data/lib/state_machine/integrations/mongo_mapper/versions.rb +89 -0
  238. data/lib/state_machine/integrations/mongoid.rb +461 -0
  239. data/lib/state_machine/integrations/mongoid/locale.rb +4 -0
  240. data/lib/state_machine/integrations/mongoid/versions.rb +81 -0
  241. data/lib/state_machine/integrations/sequel.rb +486 -0
  242. data/lib/state_machine/integrations/sequel/versions.rb +95 -0
  243. data/lib/state_machine/machine.rb +2292 -0
  244. data/lib/state_machine/machine_collection.rb +86 -0
  245. data/lib/state_machine/macro_methods.rb +522 -0
  246. data/lib/state_machine/matcher.rb +123 -0
  247. data/lib/state_machine/matcher_helpers.rb +54 -0
  248. data/lib/state_machine/node_collection.rb +222 -0
  249. data/lib/state_machine/path.rb +120 -0
  250. data/lib/state_machine/path_collection.rb +90 -0
  251. data/lib/state_machine/state.rb +297 -0
  252. data/lib/state_machine/state_collection.rb +112 -0
  253. data/lib/state_machine/state_context.rb +138 -0
  254. data/lib/state_machine/transition.rb +470 -0
  255. data/lib/state_machine/transition_collection.rb +245 -0
  256. data/lib/state_machine/version.rb +3 -0
  257. data/lib/state_machine/yard.rb +8 -0
  258. data/lib/state_machine/yard/handlers.rb +12 -0
  259. data/lib/state_machine/yard/handlers/base.rb +32 -0
  260. data/lib/state_machine/yard/handlers/event.rb +25 -0
  261. data/lib/state_machine/yard/handlers/machine.rb +344 -0
  262. data/lib/state_machine/yard/handlers/state.rb +25 -0
  263. data/lib/state_machine/yard/handlers/transition.rb +47 -0
  264. data/lib/state_machine/yard/templates.rb +3 -0
  265. data/lib/state_machine/yard/templates/default/class/html/setup.rb +30 -0
  266. data/lib/state_machine/yard/templates/default/class/html/state_machines.erb +12 -0
  267. data/lib/tasks/state_machine.rake +1 -0
  268. data/lib/tasks/state_machine.rb +30 -0
  269. data/lib/yard-state_machine.rb +2 -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']