state_machines 0.5.0 → 0.50.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +5 -5
- data/LICENSE.txt +1 -1
- data/README.md +443 -22
- data/lib/state_machines/async_mode/async_event_extensions.rb +49 -0
- data/lib/state_machines/async_mode/async_events.rb +282 -0
- data/lib/state_machines/async_mode/async_machine.rb +60 -0
- data/lib/state_machines/async_mode/async_transition_collection.rb +141 -0
- data/lib/state_machines/async_mode/thread_safe_state.rb +47 -0
- data/lib/state_machines/async_mode.rb +64 -0
- data/lib/state_machines/branch.rb +146 -86
- data/lib/state_machines/callback.rb +35 -32
- data/lib/state_machines/core.rb +3 -3
- data/lib/state_machines/core_ext/class/state_machine.rb +2 -0
- data/lib/state_machines/core_ext.rb +2 -0
- data/lib/state_machines/error.rb +7 -4
- data/lib/state_machines/eval_helpers.rb +197 -39
- data/lib/state_machines/event.rb +77 -58
- data/lib/state_machines/event_collection.rb +49 -39
- data/lib/state_machines/extensions.rb +6 -4
- data/lib/state_machines/helper_module.rb +4 -2
- data/lib/state_machines/integrations/base.rb +3 -1
- data/lib/state_machines/integrations.rb +19 -20
- data/lib/state_machines/machine/action_hooks.rb +53 -0
- data/lib/state_machines/machine/async_extensions.rb +88 -0
- data/lib/state_machines/machine/callbacks.rb +59 -0
- data/lib/state_machines/machine/class_methods.rb +97 -0
- data/lib/state_machines/machine/configuration.rb +134 -0
- data/lib/state_machines/machine/event_methods.rb +59 -0
- data/lib/state_machines/machine/helper_generators.rb +125 -0
- data/lib/state_machines/machine/integration.rb +70 -0
- data/lib/state_machines/machine/parsing.rb +77 -0
- data/lib/state_machines/machine/rendering.rb +17 -0
- data/lib/state_machines/machine/scoping.rb +44 -0
- data/lib/state_machines/machine/state_methods.rb +101 -0
- data/lib/state_machines/machine/utilities.rb +85 -0
- data/lib/state_machines/machine/validation.rb +39 -0
- data/lib/state_machines/machine.rb +425 -1011
- data/lib/state_machines/machine_collection.rb +28 -19
- data/lib/state_machines/macro_methods.rb +104 -102
- data/lib/state_machines/matcher.rb +31 -28
- data/lib/state_machines/matcher_helpers.rb +14 -12
- data/lib/state_machines/node_collection.rb +36 -29
- data/lib/state_machines/options_validator.rb +72 -0
- data/lib/state_machines/path.rb +60 -57
- data/lib/state_machines/path_collection.rb +39 -36
- data/lib/state_machines/state.rb +84 -47
- data/lib/state_machines/state_collection.rb +22 -19
- data/lib/state_machines/state_context.rb +40 -39
- data/lib/state_machines/stdio_renderer.rb +74 -0
- data/lib/state_machines/syntax_validator.rb +57 -0
- data/lib/state_machines/test_helper.rb +896 -0
- data/lib/state_machines/transition.rb +215 -199
- data/lib/state_machines/transition_collection.rb +187 -170
- data/lib/state_machines/version.rb +3 -1
- data/lib/state_machines.rb +4 -1
- metadata +39 -446
- data/.gitignore +0 -21
- data/.rspec +0 -3
- data/.ruby-gemset +0 -1
- data/.ruby-version +0 -1
- data/.travis.yml +0 -16
- data/Changelog.md +0 -22
- data/Contributors.md +0 -39
- data/Gemfile +0 -8
- data/Rakefile +0 -12
- data/Testing.md +0 -0
- data/lib/state_machines/assertions.rb +0 -40
- data/state_machines.gemspec +0 -22
- data/test/files/integrations/event_on_failure_integration.rb +0 -10
- data/test/files/integrations/vehicle.rb +0 -7
- data/test/files/models/auto_shop.rb +0 -31
- data/test/files/models/car.rb +0 -21
- data/test/files/models/driver.rb +0 -13
- data/test/files/models/model_base.rb +0 -6
- data/test/files/models/motorcycle.rb +0 -16
- data/test/files/models/traffic_light.rb +0 -47
- data/test/files/models/vehicle.rb +0 -127
- data/test/files/node.rb +0 -5
- data/test/files/switch.rb +0 -15
- data/test/functional/auto_shop_available_test.rb +0 -20
- data/test/functional/auto_shop_busy_test.rb +0 -25
- data/test/functional/car_backing_up_test.rb +0 -45
- data/test/functional/car_test.rb +0 -49
- data/test/functional/driver_default_nonstandard_test.rb +0 -13
- data/test/functional/motorcycle_test.rb +0 -52
- data/test/functional/traffic_light_caution_test.rb +0 -17
- data/test/functional/traffic_light_proceed_test.rb +0 -17
- data/test/functional/traffic_light_stop_test.rb +0 -26
- data/test/functional/vehicle_first_gear_test.rb +0 -42
- data/test/functional/vehicle_idling_test.rb +0 -59
- data/test/functional/vehicle_locked_test.rb +0 -29
- data/test/functional/vehicle_parked_test.rb +0 -53
- data/test/functional/vehicle_repaired_test.rb +0 -20
- data/test/functional/vehicle_second_gear_test.rb +0 -42
- data/test/functional/vehicle_stalled_test.rb +0 -65
- data/test/functional/vehicle_test.rb +0 -20
- data/test/functional/vehicle_third_gear_test.rb +0 -42
- data/test/functional/vehicle_unsaved_test.rb +0 -181
- data/test/functional/vehicle_with_event_attributes_test.rb +0 -30
- data/test/functional/vehicle_with_parallel_events_test.rb +0 -36
- data/test/test_helper.rb +0 -15
- data/test/unit/assertions/assert_exclusive_keys_test.rb +0 -22
- data/test/unit/assertions/assert_valid_key_test.rb +0 -12
- data/test/unit/branch/branch_test.rb +0 -28
- data/test/unit/branch/branch_with_conflicting_conditionals_test.rb +0 -27
- data/test/unit/branch/branch_with_conflicting_from_requirements_test.rb +0 -8
- data/test/unit/branch/branch_with_conflicting_on_requirements_test.rb +0 -8
- data/test/unit/branch/branch_with_conflicting_to_requirements_test.rb +0 -8
- data/test/unit/branch/branch_with_different_requirements_test.rb +0 -41
- data/test/unit/branch/branch_with_except_from_matcher_requirement_test.rb +0 -8
- data/test/unit/branch/branch_with_except_from_requirement_test.rb +0 -36
- data/test/unit/branch/branch_with_except_on_matcher_requirement_test.rb +0 -8
- data/test/unit/branch/branch_with_except_on_requirement_test.rb +0 -36
- data/test/unit/branch/branch_with_except_to_matcher_requirement_test.rb +0 -8
- data/test/unit/branch/branch_with_except_to_requirement_test.rb +0 -36
- data/test/unit/branch/branch_with_from_matcher_requirement_test.rb +0 -20
- data/test/unit/branch/branch_with_from_requirement_test.rb +0 -45
- data/test/unit/branch/branch_with_if_conditional_test.rb +0 -27
- data/test/unit/branch/branch_with_implicit_and_explicit_requirements_test.rb +0 -23
- data/test/unit/branch/branch_with_implicit_from_requirement_matcher_test.rb +0 -20
- data/test/unit/branch/branch_with_implicit_requirement_test.rb +0 -20
- data/test/unit/branch/branch_with_implicit_to_requirement_matcher_test.rb +0 -16
- data/test/unit/branch/branch_with_multiple_except_from_requirements_test.rb +0 -20
- data/test/unit/branch/branch_with_multiple_except_on_requirements_test.rb +0 -16
- data/test/unit/branch/branch_with_multiple_except_to_requirements_test.rb +0 -20
- data/test/unit/branch/branch_with_multiple_from_requirements_test.rb +0 -16
- data/test/unit/branch/branch_with_multiple_if_conditionals_test.rb +0 -20
- data/test/unit/branch/branch_with_multiple_implicit_requirements_test.rb +0 -53
- data/test/unit/branch/branch_with_multiple_to_requirements_test.rb +0 -20
- data/test/unit/branch/branch_with_multiple_unless_conditionals_test.rb +0 -20
- data/test/unit/branch/branch_with_nil_requirements_test.rb +0 -28
- data/test/unit/branch/branch_with_no_requirements_test.rb +0 -36
- data/test/unit/branch/branch_with_on_matcher_requirement_test.rb +0 -16
- data/test/unit/branch/branch_with_on_requirement_test.rb +0 -45
- data/test/unit/branch/branch_with_to_matcher_requirement_test.rb +0 -20
- data/test/unit/branch/branch_with_to_requirement_test.rb +0 -45
- data/test/unit/branch/branch_with_unless_conditional_test.rb +0 -27
- data/test/unit/branch/branch_without_guards_test.rb +0 -27
- data/test/unit/callback/callback_by_default_test.rb +0 -25
- data/test/unit/callback/callback_test.rb +0 -53
- data/test/unit/callback/callback_with_application_bound_object_test.rb +0 -23
- data/test/unit/callback/callback_with_application_terminator_test.rb +0 -24
- data/test/unit/callback/callback_with_arguments_test.rb +0 -14
- data/test/unit/callback/callback_with_around_type_and_arguments_test.rb +0 -25
- data/test/unit/callback/callback_with_around_type_and_block_test.rb +0 -44
- data/test/unit/callback/callback_with_around_type_and_bound_method_test.rb +0 -23
- data/test/unit/callback/callback_with_around_type_and_multiple_methods_test.rb +0 -93
- data/test/unit/callback/callback_with_around_type_and_terminator_test.rb +0 -17
- data/test/unit/callback/callback_with_block_test.rb +0 -20
- data/test/unit/callback/callback_with_bound_method_and_arguments_test.rb +0 -28
- data/test/unit/callback/callback_with_bound_method_test.rb +0 -35
- data/test/unit/callback/callback_with_do_method_test.rb +0 -18
- data/test/unit/callback/callback_with_explicit_requirements_test.rb +0 -32
- data/test/unit/callback/callback_with_if_condition_test.rb +0 -17
- data/test/unit/callback/callback_with_implicit_requirements_test.rb +0 -32
- data/test/unit/callback/callback_with_method_argument_test.rb +0 -18
- data/test/unit/callback/callback_with_mixed_methods_test.rb +0 -31
- data/test/unit/callback/callback_with_multiple_bound_methods_test.rb +0 -21
- data/test/unit/callback/callback_with_multiple_do_methods_test.rb +0 -29
- data/test/unit/callback/callback_with_multiple_method_arguments_test.rb +0 -29
- data/test/unit/callback/callback_with_terminator_test.rb +0 -22
- data/test/unit/callback/callback_with_unbound_method_test.rb +0 -14
- data/test/unit/callback/callback_with_unless_condition_test.rb +0 -17
- data/test/unit/callback/callback_without_arguments_test.rb +0 -14
- data/test/unit/callback/callback_without_terminator_test.rb +0 -12
- data/test/unit/error/error_by_default_test.rb +0 -21
- data/test/unit/error/error_with_message_test.rb +0 -23
- data/test/unit/eval_helper/eval_helpers_base_test.rb +0 -8
- data/test/unit/eval_helper/eval_helpers_proc_block_and_explicit_arguments_test.rb +0 -14
- data/test/unit/eval_helper/eval_helpers_proc_block_and_implicit_arguments_test.rb +0 -14
- data/test/unit/eval_helper/eval_helpers_proc_test.rb +0 -13
- data/test/unit/eval_helper/eval_helpers_proc_with_arguments_test.rb +0 -13
- data/test/unit/eval_helper/eval_helpers_proc_with_block_test.rb +0 -13
- data/test/unit/eval_helper/eval_helpers_proc_with_block_without_arguments_test.rb +0 -18
- data/test/unit/eval_helper/eval_helpers_proc_with_block_without_object_test.rb +0 -14
- data/test/unit/eval_helper/eval_helpers_proc_without_arguments_test.rb +0 -19
- data/test/unit/eval_helper/eval_helpers_string_test.rb +0 -25
- data/test/unit/eval_helper/eval_helpers_string_with_block_test.rb +0 -12
- data/test/unit/eval_helper/eval_helpers_symbol_method_missing_test.rb +0 -20
- data/test/unit/eval_helper/eval_helpers_symbol_private_test.rb +0 -17
- data/test/unit/eval_helper/eval_helpers_symbol_protected_test.rb +0 -17
- data/test/unit/eval_helper/eval_helpers_symbol_tainted_method_test.rb +0 -18
- data/test/unit/eval_helper/eval_helpers_symbol_test.rb +0 -16
- data/test/unit/eval_helper/eval_helpers_symbol_with_arguments_and_block_test.rb +0 -16
- data/test/unit/eval_helper/eval_helpers_symbol_with_arguments_test.rb +0 -16
- data/test/unit/eval_helper/eval_helpers_symbol_with_block_test.rb +0 -16
- data/test/unit/eval_helper/eval_helpers_test.rb +0 -13
- data/test/unit/event/event_after_being_copied_test.rb +0 -17
- data/test/unit/event/event_by_default_test.rb +0 -60
- data/test/unit/event/event_context_test.rb +0 -16
- data/test/unit/event/event_on_failure_test.rb +0 -44
- data/test/unit/event/event_test.rb +0 -34
- data/test/unit/event/event_transitions_test.rb +0 -62
- data/test/unit/event/event_with_conflicting_helpers_after_definition_test.rb +0 -79
- data/test/unit/event/event_with_conflicting_helpers_before_definition_test.rb +0 -58
- data/test/unit/event/event_with_conflicting_machine_test.rb +0 -48
- data/test/unit/event/event_with_dynamic_human_name_test.rb +0 -26
- data/test/unit/event/event_with_human_name_test.rb +0 -13
- data/test/unit/event/event_with_invalid_current_state_test.rb +0 -30
- data/test/unit/event/event_with_machine_action_test.rb +0 -33
- data/test/unit/event/event_with_marshalling_test.rb +0 -47
- data/test/unit/event/event_with_matching_disabled_transitions_test.rb +0 -115
- data/test/unit/event/event_with_matching_enabled_transitions_test.rb +0 -75
- data/test/unit/event/event_with_multiple_transitions_test.rb +0 -61
- data/test/unit/event/event_with_namespace_test.rb +0 -34
- data/test/unit/event/event_with_transition_with_blacklisted_to_state_test.rb +0 -60
- data/test/unit/event/event_with_transition_with_loopback_state_test.rb +0 -36
- data/test/unit/event/event_with_transition_with_nil_to_state_test.rb +0 -36
- data/test/unit/event/event_with_transition_with_whitelisted_to_state_test.rb +0 -51
- data/test/unit/event/event_with_transition_without_to_state_test.rb +0 -36
- data/test/unit/event/event_with_transitions_test.rb +0 -32
- data/test/unit/event/event_without_matching_transitions_test.rb +0 -41
- data/test/unit/event/event_without_transitions_test.rb +0 -28
- data/test/unit/event/invalid_event_test.rb +0 -20
- data/test/unit/event_collection/event_collection_attribute_with_machine_action_test.rb +0 -62
- data/test/unit/event_collection/event_collection_attribute_with_namespaced_machine_test.rb +0 -36
- data/test/unit/event_collection/event_collection_by_default_test.rb +0 -26
- data/test/unit/event_collection/event_collection_test.rb +0 -39
- data/test/unit/event_collection/event_collection_with_custom_machine_attribute_test.rb +0 -31
- data/test/unit/event_collection/event_collection_with_events_with_transitions_test.rb +0 -76
- data/test/unit/event_collection/event_collection_with_multiple_events_test.rb +0 -27
- data/test/unit/event_collection/event_collection_with_validations_test.rb +0 -74
- data/test/unit/event_collection/event_collection_without_machine_action_test.rb +0 -18
- data/test/unit/event_collection/event_string_collection_test.rb +0 -31
- data/test/unit/helper_module_test.rb +0 -17
- data/test/unit/integrations/integration_finder_test.rb +0 -16
- data/test/unit/integrations/integration_matcher_test.rb +0 -29
- data/test/unit/invalid_transition/invalid_parallel_transition_test.rb +0 -18
- data/test/unit/invalid_transition/invalid_transition_test.rb +0 -47
- data/test/unit/invalid_transition/invalid_transition_with_integration_test.rb +0 -45
- data/test/unit/invalid_transition/invalid_transition_with_namespace_test.rb +0 -32
- data/test/unit/machine/machine_after_being_copied_test.rb +0 -62
- data/test/unit/machine/machine_after_changing_initial_state.rb +0 -28
- data/test/unit/machine/machine_after_changing_owner_class_test.rb +0 -31
- data/test/unit/machine/machine_by_default_test.rb +0 -160
- data/test/unit/machine/machine_finder_custom_options_test.rb +0 -17
- data/test/unit/machine/machine_finder_with_existing_machine_on_superclass_test.rb +0 -85
- data/test/unit/machine/machine_finder_with_existing_on_same_class_test.rb +0 -23
- data/test/unit/machine/machine_finder_without_existing_machine_test.rb +0 -25
- data/test/unit/machine/machine_persistence_test.rb +0 -52
- data/test/unit/machine/machine_state_initialization_test.rb +0 -56
- data/test/unit/machine/machine_test.rb +0 -30
- data/test/unit/machine/machine_with_action_already_overridden_test.rb +0 -23
- data/test/unit/machine/machine_with_action_defined_in_class_test.rb +0 -37
- data/test/unit/machine/machine_with_action_defined_in_included_module_test.rb +0 -46
- data/test/unit/machine/machine_with_action_defined_in_superclass_test.rb +0 -43
- data/test/unit/machine/machine_with_action_undefined_test.rb +0 -33
- data/test/unit/machine/machine_with_cached_state_test.rb +0 -20
- data/test/unit/machine/machine_with_class_helpers_test.rb +0 -179
- data/test/unit/machine/machine_with_conflicting_helpers_after_definition_test.rb +0 -244
- data/test/unit/machine/machine_with_conflicting_helpers_before_definition_test.rb +0 -175
- data/test/unit/machine/machine_with_custom_action_test.rb +0 -11
- data/test/unit/machine/machine_with_custom_attribute_test.rb +0 -103
- data/test/unit/machine/machine_with_custom_initialize_test.rb +0 -24
- data/test/unit/machine/machine_with_custom_integration_test.rb +0 -72
- data/test/unit/machine/machine_with_custom_invalidation_test.rb +0 -39
- data/test/unit/machine/machine_with_custom_name_test.rb +0 -57
- data/test/unit/machine/machine_with_custom_plural_test.rb +0 -52
- data/test/unit/machine/machine_with_dynamic_initial_state_test.rb +0 -65
- data/test/unit/machine/machine_with_event_matchers_test.rb +0 -41
- data/test/unit/machine/machine_with_events_test.rb +0 -52
- data/test/unit/machine/machine_with_events_with_custom_human_names_test.rb +0 -18
- data/test/unit/machine/machine_with_events_with_transitions_test.rb +0 -37
- data/test/unit/machine/machine_with_existing_event_test.rb +0 -17
- data/test/unit/machine/machine_with_existing_machines_on_owner_class_test.rb +0 -20
- data/test/unit/machine/machine_with_existing_machines_with_same_attributes_on_owner_class_test.rb +0 -71
- data/test/unit/machine/machine_with_existing_machines_with_same_attributes_on_owner_subclass_test.rb +0 -31
- data/test/unit/machine/machine_with_existing_state_test.rb +0 -27
- data/test/unit/machine/machine_with_failure_callbacks_test.rb +0 -48
- data/test/unit/machine/machine_with_helpers_test.rb +0 -14
- data/test/unit/machine/machine_with_initial_state_with_value_and_owner_default.rb +0 -25
- data/test/unit/machine/machine_with_initialize_and_super_test.rb +0 -17
- data/test/unit/machine/machine_with_initialize_arguments_and_block_test.rb +0 -31
- data/test/unit/machine/machine_with_initialize_without_super_test.rb +0 -17
- data/test/unit/machine/machine_with_instance_helpers_test.rb +0 -179
- data/test/unit/machine/machine_with_integration_test.rb +0 -72
- data/test/unit/machine/machine_with_multiple_events_test.rb +0 -32
- data/test/unit/machine/machine_with_namespace_test.rb +0 -48
- data/test/unit/machine/machine_with_nil_action_test.rb +0 -27
- data/test/unit/machine/machine_with_other_states.rb +0 -22
- data/test/unit/machine/machine_with_owner_subclass_test.rb +0 -18
- data/test/unit/machine/machine_with_paths_test.rb +0 -25
- data/test/unit/machine/machine_with_private_action_test.rb +0 -43
- data/test/unit/machine/machine_with_state_matchers_test.rb +0 -41
- data/test/unit/machine/machine_with_state_with_matchers_test.rb +0 -19
- data/test/unit/machine/machine_with_states_test.rb +0 -55
- data/test/unit/machine/machine_with_states_with_behaviors_test.rb +0 -23
- data/test/unit/machine/machine_with_states_with_custom_human_names_test.rb +0 -18
- data/test/unit/machine/machine_with_states_with_custom_values_test.rb +0 -21
- data/test/unit/machine/machine_with_states_with_runtime_dependencies_test.rb +0 -19
- data/test/unit/machine/machine_with_static_initial_state_test.rb +0 -49
- data/test/unit/machine/machine_with_superclass_conflicting_helpers_after_definition_test.rb +0 -36
- data/test/unit/machine/machine_with_transition_callbacks_test.rb +0 -144
- data/test/unit/machine/machine_with_transitions_test.rb +0 -87
- data/test/unit/machine/machine_without_initialization_test.rb +0 -31
- data/test/unit/machine/machine_without_initialize_test.rb +0 -14
- data/test/unit/machine/machine_without_integration_test.rb +0 -31
- data/test/unit/machine_collection/machine_collection_by_default_test.rb +0 -11
- data/test/unit/machine_collection/machine_collection_fire_test.rb +0 -80
- data/test/unit/machine_collection/machine_collection_fire_with_transactions_test.rb +0 -54
- data/test/unit/machine_collection/machine_collection_fire_with_validations_test.rb +0 -76
- data/test/unit/machine_collection/machine_collection_state_initialization_test.rb +0 -111
- data/test/unit/machine_collection/machine_collection_transitions_with_blank_events_test.rb +0 -25
- data/test/unit/machine_collection/machine_collection_transitions_with_custom_options_test.rb +0 -20
- data/test/unit/machine_collection/machine_collection_transitions_with_different_actions_test.rb +0 -26
- data/test/unit/machine_collection/machine_collection_transitions_with_exisiting_transitions_test.rb +0 -25
- data/test/unit/machine_collection/machine_collection_transitions_with_invalid_events_test.rb +0 -25
- data/test/unit/machine_collection/machine_collection_transitions_with_same_actions_test.rb +0 -31
- data/test/unit/machine_collection/machine_collection_transitions_with_transition_test.rb +0 -26
- data/test/unit/machine_collection/machine_collection_transitions_without_events_test.rb +0 -25
- data/test/unit/machine_collection/machine_collection_transitions_without_transition_test.rb +0 -27
- data/test/unit/matcher/all_matcher_test.rb +0 -29
- data/test/unit/matcher/blacklist_matcher_test.rb +0 -30
- data/test/unit/matcher/loopback_matcher_test.rb +0 -27
- data/test/unit/matcher/matcher_by_default_test.rb +0 -15
- data/test/unit/matcher/matcher_with_multiple_values_test.rb +0 -15
- data/test/unit/matcher/matcher_with_value_test.rb +0 -15
- data/test/unit/matcher/whitelist_matcher_test.rb +0 -30
- data/test/unit/matcher_helpers/matcher_helpers_all_test.rb +0 -14
- data/test/unit/matcher_helpers/matcher_helpers_any_test.rb +0 -14
- data/test/unit/matcher_helpers/matcher_helpers_same_test.rb +0 -13
- data/test/unit/node_collection/node_collection_after_being_copied_test.rb +0 -46
- data/test/unit/node_collection/node_collection_after_update_test.rb +0 -36
- data/test/unit/node_collection/node_collection_by_default_test.rb +0 -22
- data/test/unit/node_collection/node_collection_test.rb +0 -23
- data/test/unit/node_collection/node_collection_with_indices_test.rb +0 -42
- data/test/unit/node_collection/node_collection_with_matcher_contexts_test.rb +0 -25
- data/test/unit/node_collection/node_collection_with_nodes_test.rb +0 -46
- data/test/unit/node_collection/node_collection_with_numeric_index_test.rb +0 -24
- data/test/unit/node_collection/node_collection_with_postdefined_contexts_test.rb +0 -22
- data/test/unit/node_collection/node_collection_with_predefined_contexts_test.rb +0 -23
- data/test/unit/node_collection/node_collection_with_string_index_test.rb +0 -20
- data/test/unit/node_collection/node_collection_with_symbol_index_test.rb +0 -20
- data/test/unit/node_collection/node_collection_without_indices_test.rb +0 -30
- data/test/unit/path/path_by_default_test.rb +0 -54
- data/test/unit/path/path_test.rb +0 -14
- data/test/unit/path/path_with_available_transitions_after_reaching_target_test.rb +0 -40
- data/test/unit/path/path_with_available_transitions_test.rb +0 -54
- data/test/unit/path/path_with_deep_target_reached_test.rb +0 -50
- data/test/unit/path/path_with_deep_target_test.rb +0 -40
- data/test/unit/path/path_with_duplicates_test.rb +0 -32
- data/test/unit/path/path_with_encountered_transitions_test.rb +0 -34
- data/test/unit/path/path_with_guarded_transitions_test.rb +0 -42
- data/test/unit/path/path_with_reached_target_test.rb +0 -35
- data/test/unit/path/path_with_transitions_test.rb +0 -54
- data/test/unit/path/path_with_unreached_target_test.rb +0 -31
- data/test/unit/path/path_without_transitions_test.rb +0 -24
- data/test/unit/path_collection/path_collection_by_default_test.rb +0 -46
- data/test/unit/path_collection/path_collection_test.rb +0 -24
- data/test/unit/path_collection/path_collection_with_deep_paths_test.rb +0 -43
- data/test/unit/path_collection/path_collection_with_duplicate_nodes_test.rb +0 -31
- data/test/unit/path_collection/path_collection_with_from_state_test.rb +0 -27
- data/test/unit/path_collection/path_collection_with_paths_test.rb +0 -47
- data/test/unit/path_collection/path_collection_with_to_state_test.rb +0 -29
- data/test/unit/path_collection/path_with_guarded_paths_test.rb +0 -25
- data/test/unit/state/state_after_being_copied_test.rb +0 -19
- data/test/unit/state/state_by_default_test.rb +0 -41
- data/test/unit/state/state_final_test.rb +0 -28
- data/test/unit/state/state_initial_test.rb +0 -13
- data/test/unit/state/state_not_final_test.rb +0 -32
- data/test/unit/state/state_not_initial_test.rb +0 -13
- data/test/unit/state/state_test.rb +0 -44
- data/test/unit/state/state_with_cached_lambda_value_test.rb +0 -29
- data/test/unit/state/state_with_conflicting_helpers_after_definition_test.rb +0 -38
- data/test/unit/state/state_with_conflicting_helpers_before_definition_test.rb +0 -29
- data/test/unit/state/state_with_conflicting_machine_name_test.rb +0 -20
- data/test/unit/state/state_with_conflicting_machine_test.rb +0 -37
- data/test/unit/state/state_with_context_test.rb +0 -60
- data/test/unit/state/state_with_dynamic_human_name_test.rb +0 -25
- data/test/unit/state/state_with_existing_context_method_test.rb +0 -24
- data/test/unit/state/state_with_human_name_test.rb +0 -13
- data/test/unit/state/state_with_integer_value_test.rb +0 -32
- data/test/unit/state/state_with_invalid_method_call_test.rb +0 -21
- data/test/unit/state/state_with_lambda_value_test.rb +0 -37
- data/test/unit/state/state_with_matcher_test.rb +0 -18
- data/test/unit/state/state_with_multiple_contexts_test.rb +0 -57
- data/test/unit/state/state_with_name_test.rb +0 -43
- data/test/unit/state/state_with_namespace_test.rb +0 -22
- data/test/unit/state/state_with_nil_value_test.rb +0 -35
- data/test/unit/state/state_with_redefined_context_method_test.rb +0 -45
- data/test/unit/state/state_with_symbolic_value_test.rb +0 -32
- data/test/unit/state/state_with_valid_inherited_method_call_for_current_state_test.rb +0 -40
- data/test/unit/state/state_with_valid_method_call_for_current_state_test.rb +0 -33
- data/test/unit/state/state_with_valid_method_call_for_different_state_test.rb +0 -41
- data/test/unit/state/state_without_cached_lambda_value_test.rb +0 -25
- data/test/unit/state/state_without_name_test.rb +0 -39
- data/test/unit/state_collection/state_collection_by_default_test.rb +0 -21
- data/test/unit/state_collection/state_collection_string_test.rb +0 -35
- data/test/unit/state_collection/state_collection_test.rb +0 -74
- data/test/unit/state_collection/state_collection_with_custom_state_values_test.rb +0 -29
- data/test/unit/state_collection/state_collection_with_event_transitions_test.rb +0 -39
- data/test/unit/state_collection/state_collection_with_initial_state_test.rb +0 -40
- data/test/unit/state_collection/state_collection_with_namespace_test.rb +0 -21
- data/test/unit/state_collection/state_collection_with_state_behaviors_test.rb +0 -40
- data/test/unit/state_collection/state_collection_with_state_matchers_test.rb +0 -29
- data/test/unit/state_collection/state_collection_with_transition_callbacks_test.rb +0 -40
- data/test/unit/state_context/state_context_proxy_test.rb +0 -26
- data/test/unit/state_context/state_context_proxy_with_if_and_unless_conditions_test.rb +0 -42
- data/test/unit/state_context/state_context_proxy_with_if_condition_test.rb +0 -64
- data/test/unit/state_context/state_context_proxy_with_multiple_if_conditions_test.rb +0 -32
- data/test/unit/state_context/state_context_proxy_with_multiple_unless_conditions_test.rb +0 -32
- data/test/unit/state_context/state_context_proxy_with_unless_condition_test.rb +0 -64
- data/test/unit/state_context/state_context_proxy_without_conditions_test.rb +0 -31
- data/test/unit/state_context/state_context_test.rb +0 -28
- data/test/unit/state_context/state_context_transition_test.rb +0 -104
- data/test/unit/state_context/state_context_with_matching_transition_test.rb +0 -27
- data/test/unit/state_machine/state_machine_by_default_test.rb +0 -12
- data/test/unit/state_machine/state_machine_test.rb +0 -20
- data/test/unit/transition/transition_after_being_performed_test.rb +0 -48
- data/test/unit/transition/transition_after_being_persisted_test.rb +0 -46
- data/test/unit/transition/transition_after_being_rolled_back_test.rb +0 -35
- data/test/unit/transition/transition_equality_test.rb +0 -52
- data/test/unit/transition/transition_loopback_test.rb +0 -18
- data/test/unit/transition/transition_test.rb +0 -96
- data/test/unit/transition/transition_transient_test.rb +0 -20
- data/test/unit/transition/transition_with_action_test.rb +0 -27
- data/test/unit/transition/transition_with_after_callbacks_skipped_test.rb +0 -127
- data/test/unit/transition/transition_with_after_callbacks_test.rb +0 -93
- data/test/unit/transition/transition_with_around_callbacks_test.rb +0 -141
- data/test/unit/transition/transition_with_before_callbacks_skipped_test.rb +0 -30
- data/test/unit/transition/transition_with_before_callbacks_test.rb +0 -104
- data/test/unit/transition/transition_with_custom_machine_attribute_test.rb +0 -28
- data/test/unit/transition/transition_with_different_states_test.rb +0 -18
- data/test/unit/transition/transition_with_dynamic_to_value_test.rb +0 -19
- data/test/unit/transition/transition_with_failure_callbacks_test.rb +0 -84
- data/test/unit/transition/transition_with_invalid_nodes_test.rb +0 -29
- data/test/unit/transition/transition_with_mixed_callbacks_test.rb +0 -105
- data/test/unit/transition/transition_with_multiple_after_callbacks_test.rb +0 -40
- data/test/unit/transition/transition_with_multiple_around_callbacks_test.rb +0 -114
- data/test/unit/transition/transition_with_multiple_before_callbacks_test.rb +0 -40
- data/test/unit/transition/transition_with_multiple_failure_callbacks_test.rb +0 -40
- data/test/unit/transition/transition_with_namespace_test.rb +0 -47
- data/test/unit/transition/transition_with_perform_arguments_test.rb +0 -35
- data/test/unit/transition/transition_with_transactions_test.rb +0 -42
- data/test/unit/transition/transition_without_callbacks_test.rb +0 -33
- data/test/unit/transition/transition_without_reading_state_test.rb +0 -22
- data/test/unit/transition/transition_without_running_action_test.rb +0 -47
- data/test/unit/transition_collection/attribute_transition_collection_by_default_test.rb +0 -23
- data/test/unit/transition_collection/attribute_transition_collection_marshalling_test.rb +0 -64
- data/test/unit/transition_collection/attribute_transition_collection_with_action_error_test.rb +0 -44
- data/test/unit/transition_collection/attribute_transition_collection_with_action_failed_test.rb +0 -44
- data/test/unit/transition_collection/attribute_transition_collection_with_after_callback_error_test.rb +0 -32
- data/test/unit/transition_collection/attribute_transition_collection_with_after_callback_halt_test.rb +0 -33
- data/test/unit/transition_collection/attribute_transition_collection_with_around_after_yield_callback_error_test.rb +0 -32
- data/test/unit/transition_collection/attribute_transition_collection_with_around_callback_after_yield_error_test.rb +0 -32
- data/test/unit/transition_collection/attribute_transition_collection_with_around_callback_after_yield_halt_test.rb +0 -33
- data/test/unit/transition_collection/attribute_transition_collection_with_around_callback_before_yield_halt_test.rb +0 -33
- data/test/unit/transition_collection/attribute_transition_collection_with_before_callback_error_test.rb +0 -32
- data/test/unit/transition_collection/attribute_transition_collection_with_before_callback_halt_test.rb +0 -33
- data/test/unit/transition_collection/attribute_transition_collection_with_callbacks_test.rb +0 -68
- data/test/unit/transition_collection/attribute_transition_collection_with_event_transitions_test.rb +0 -41
- data/test/unit/transition_collection/attribute_transition_collection_with_events_test.rb +0 -44
- data/test/unit/transition_collection/attribute_transition_collection_with_skipped_after_callbacks_test.rb +0 -42
- data/test/unit/transition_collection/transition_collection_by_default_test.rb +0 -23
- data/test/unit/transition_collection/transition_collection_empty_with_block_test.rb +0 -23
- data/test/unit/transition_collection/transition_collection_empty_without_block_test.rb +0 -12
- data/test/unit/transition_collection/transition_collection_invalid_test.rb +0 -21
- data/test/unit/transition_collection/transition_collection_partial_invalid_test.rb +0 -69
- data/test/unit/transition_collection/transition_collection_test.rb +0 -26
- data/test/unit/transition_collection/transition_collection_valid_test.rb +0 -57
- data/test/unit/transition_collection/transition_collection_with_action_error_test.rb +0 -66
- data/test/unit/transition_collection/transition_collection_with_action_failed_test.rb +0 -60
- data/test/unit/transition_collection/transition_collection_with_action_hook_and_block_test.rb +0 -17
- data/test/unit/transition_collection/transition_collection_with_action_hook_and_skipped_action_test.rb +0 -17
- data/test/unit/transition_collection/transition_collection_with_action_hook_and_skipped_after_callbacks_test.rb +0 -37
- data/test/unit/transition_collection/transition_collection_with_action_hook_base_test.rb +0 -34
- data/test/unit/transition_collection/transition_collection_with_action_hook_error_test.rb +0 -29
- data/test/unit/transition_collection/transition_collection_with_action_hook_invalid_test.rb +0 -17
- data/test/unit/transition_collection/transition_collection_with_action_hook_multiple_test.rb +0 -79
- data/test/unit/transition_collection/transition_collection_with_action_hook_test.rb +0 -45
- data/test/unit/transition_collection/transition_collection_with_action_hook_with_different_actions_test.rb +0 -48
- data/test/unit/transition_collection/transition_collection_with_action_hook_with_nil_action_test.rb +0 -42
- data/test/unit/transition_collection/transition_collection_with_after_callback_halt_test.rb +0 -47
- data/test/unit/transition_collection/transition_collection_with_before_callback_halt_test.rb +0 -51
- data/test/unit/transition_collection/transition_collection_with_block_test.rb +0 -46
- data/test/unit/transition_collection/transition_collection_with_callbacks_test.rb +0 -135
- data/test/unit/transition_collection/transition_collection_with_different_actions_test.rb +0 -189
- data/test/unit/transition_collection/transition_collection_with_duplicate_actions_test.rb +0 -48
- data/test/unit/transition_collection/transition_collection_with_empty_actions_test.rb +0 -41
- data/test/unit/transition_collection/transition_collection_with_mixed_actions_test.rb +0 -41
- data/test/unit/transition_collection/transition_collection_with_skipped_actions_and_block_test.rb +0 -34
- data/test/unit/transition_collection/transition_collection_with_skipped_actions_test.rb +0 -69
- data/test/unit/transition_collection/transition_collection_with_skipped_after_callbacks_and_around_callbacks_test.rb +0 -53
- data/test/unit/transition_collection/transition_collection_with_skipped_after_callbacks_test.rb +0 -34
- data/test/unit/transition_collection/transition_collection_with_transactions_test.rb +0 -65
- data/test/unit/transition_collection/transition_collection_without_transactions_test.rb +0 -29
@@ -0,0 +1,896 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module StateMachines
|
4
|
+
# Test helper module providing assertion methods for state machine testing
|
5
|
+
# Designed to work with Minitest, RSpec, and future testing frameworks
|
6
|
+
#
|
7
|
+
# @example Basic usage with Minitest
|
8
|
+
# class MyModelTest < Minitest::Test
|
9
|
+
# include StateMachines::TestHelper
|
10
|
+
#
|
11
|
+
# def test_initial_state
|
12
|
+
# model = MyModel.new
|
13
|
+
# assert_state(model, :state_machine_name, :initial_state)
|
14
|
+
# end
|
15
|
+
# end
|
16
|
+
#
|
17
|
+
# @example Usage with RSpec
|
18
|
+
# RSpec.describe MyModel do
|
19
|
+
# include StateMachines::TestHelper
|
20
|
+
#
|
21
|
+
# it "starts in initial state" do
|
22
|
+
# model = MyModel.new
|
23
|
+
# assert_state(model, :state_machine_name, :initial_state)
|
24
|
+
# end
|
25
|
+
# end
|
26
|
+
#
|
27
|
+
# @since 0.10.0
|
28
|
+
module TestHelper
|
29
|
+
# Assert that an object is in a specific state for a given state machine
|
30
|
+
#
|
31
|
+
# @param object [Object] The object with state machines
|
32
|
+
# @param expected_state [Symbol] The expected state
|
33
|
+
# @param machine_name [Symbol] The name of the state machine (defaults to :state)
|
34
|
+
# @param message [String, nil] Custom failure message
|
35
|
+
# @return [void]
|
36
|
+
# @raise [AssertionError] If the state doesn't match
|
37
|
+
#
|
38
|
+
# @example
|
39
|
+
# user = User.new
|
40
|
+
# assert_sm_state(user, :active) # Uses default :state machine
|
41
|
+
# assert_sm_state(user, :active, machine_name: :status) # Uses :status machine
|
42
|
+
def assert_sm_state(object, expected_state, machine_name: :state, message: nil)
|
43
|
+
name_method = "#{machine_name}_name"
|
44
|
+
|
45
|
+
# Handle the case where machine_name doesn't have a corresponding _name method
|
46
|
+
unless object.respond_to?(name_method)
|
47
|
+
available_machines = begin
|
48
|
+
object.class.state_machines.keys
|
49
|
+
rescue StandardError
|
50
|
+
[]
|
51
|
+
end
|
52
|
+
raise ArgumentError, "No state machine '#{machine_name}' found. Available machines: #{available_machines.inspect}"
|
53
|
+
end
|
54
|
+
|
55
|
+
actual = object.send(name_method)
|
56
|
+
default_message = "Expected #{object.class}##{machine_name} to be #{expected_state}, but was #{actual}"
|
57
|
+
|
58
|
+
if defined?(::Minitest)
|
59
|
+
assert_equal expected_state.to_s, actual.to_s, message || default_message
|
60
|
+
elsif defined?(::RSpec)
|
61
|
+
expect(actual.to_s).to eq(expected_state.to_s), message || default_message
|
62
|
+
else
|
63
|
+
raise "Expected #{expected_state}, but got #{actual}" unless expected_state.to_s == actual.to_s
|
64
|
+
end
|
65
|
+
end
|
66
|
+
|
67
|
+
# Assert that an object can transition via a specific event
|
68
|
+
#
|
69
|
+
# @param object [Object] The object with state machines
|
70
|
+
# @param event [Symbol] The event name
|
71
|
+
# @param machine_name [Symbol] The name of the state machine (defaults to :state)
|
72
|
+
# @param message [String, nil] Custom failure message
|
73
|
+
# @return [void]
|
74
|
+
# @raise [AssertionError] If the transition is not available
|
75
|
+
#
|
76
|
+
# @example
|
77
|
+
# user = User.new
|
78
|
+
# assert_sm_can_transition(user, :activate) # Uses default :state machine
|
79
|
+
# assert_sm_can_transition(user, :activate, machine_name: :status) # Uses :status machine
|
80
|
+
def assert_sm_can_transition(object, event, machine_name: :state, message: nil)
|
81
|
+
# Try different method naming patterns
|
82
|
+
possible_methods = [
|
83
|
+
"can_#{event}?", # Default state machine or non-namespaced
|
84
|
+
"can_#{event}_#{machine_name}?" # Namespaced events
|
85
|
+
]
|
86
|
+
|
87
|
+
can_method = possible_methods.find { |method| object.respond_to?(method) }
|
88
|
+
|
89
|
+
unless can_method
|
90
|
+
available_methods = object.methods.grep(/^can_.*\?$/).sort
|
91
|
+
raise ArgumentError, "No transition method found for event :#{event} on machine :#{machine_name}. Available methods: #{available_methods.first(10).inspect}"
|
92
|
+
end
|
93
|
+
|
94
|
+
default_message = "Expected to be able to trigger event :#{event} on #{machine_name}, but #{can_method} returned false"
|
95
|
+
|
96
|
+
if defined?(::Minitest)
|
97
|
+
assert object.send(can_method), message || default_message
|
98
|
+
elsif defined?(::RSpec)
|
99
|
+
expect(object.send(can_method)).to be_truthy, message || default_message
|
100
|
+
else
|
101
|
+
raise default_message unless object.send(can_method)
|
102
|
+
end
|
103
|
+
end
|
104
|
+
|
105
|
+
# Assert that an object cannot transition via a specific event
|
106
|
+
#
|
107
|
+
# @param object [Object] The object with state machines
|
108
|
+
# @param event [Symbol] The event name
|
109
|
+
# @param machine_name [Symbol] The name of the state machine (defaults to :state)
|
110
|
+
# @param message [String, nil] Custom failure message
|
111
|
+
# @return [void]
|
112
|
+
# @raise [AssertionError] If the transition is available
|
113
|
+
#
|
114
|
+
# @example
|
115
|
+
# user = User.new
|
116
|
+
# assert_sm_cannot_transition(user, :delete) # Uses default :state machine
|
117
|
+
# assert_sm_cannot_transition(user, :delete, machine_name: :status) # Uses :status machine
|
118
|
+
def assert_sm_cannot_transition(object, event, machine_name: :state, message: nil)
|
119
|
+
# Try different method naming patterns
|
120
|
+
possible_methods = [
|
121
|
+
"can_#{event}?", # Default state machine or non-namespaced
|
122
|
+
"can_#{event}_#{machine_name}?" # Namespaced events
|
123
|
+
]
|
124
|
+
|
125
|
+
can_method = possible_methods.find { |method| object.respond_to?(method) }
|
126
|
+
|
127
|
+
unless can_method
|
128
|
+
available_methods = object.methods.grep(/^can_.*\?$/).sort
|
129
|
+
raise ArgumentError, "No transition method found for event :#{event} on machine :#{machine_name}. Available methods: #{available_methods.first(10).inspect}"
|
130
|
+
end
|
131
|
+
|
132
|
+
default_message = "Expected not to be able to trigger event :#{event} on #{machine_name}, but #{can_method} returned true"
|
133
|
+
|
134
|
+
if defined?(::Minitest)
|
135
|
+
refute object.send(can_method), message || default_message
|
136
|
+
elsif defined?(::RSpec)
|
137
|
+
expect(object.send(can_method)).to be_falsy, message || default_message
|
138
|
+
elsif object.send(can_method)
|
139
|
+
raise default_message
|
140
|
+
end
|
141
|
+
end
|
142
|
+
|
143
|
+
# Assert that triggering an event changes the object to the expected state
|
144
|
+
#
|
145
|
+
# @param object [Object] The object with state machines
|
146
|
+
# @param event [Symbol] The event to trigger
|
147
|
+
# @param expected_state [Symbol] The expected state after transition
|
148
|
+
# @param machine_name [Symbol] The name of the state machine (defaults to :state)
|
149
|
+
# @param message [String, nil] Custom failure message
|
150
|
+
# @return [void]
|
151
|
+
# @raise [AssertionError] If the transition fails or results in wrong state
|
152
|
+
#
|
153
|
+
# @example
|
154
|
+
# user = User.new
|
155
|
+
# assert_sm_transition(user, :activate, :active) # Uses default :state machine
|
156
|
+
# assert_sm_transition(user, :activate, :active, machine_name: :status) # Uses :status machine
|
157
|
+
def assert_sm_transition(object, event, expected_state, machine_name: :state, message: nil)
|
158
|
+
object.send("#{event}!")
|
159
|
+
assert_sm_state(object, expected_state, machine_name: machine_name, message: message)
|
160
|
+
end
|
161
|
+
|
162
|
+
# === Extended State Machine Assertions ===
|
163
|
+
|
164
|
+
def assert_sm_states_list(machine, expected_states, message = nil)
|
165
|
+
actual_states = machine.states.map(&:name).compact
|
166
|
+
default_message = "Expected states #{expected_states} but got #{actual_states}"
|
167
|
+
|
168
|
+
if defined?(::Minitest)
|
169
|
+
assert_equal expected_states.sort, actual_states.sort, message || default_message
|
170
|
+
elsif defined?(::RSpec)
|
171
|
+
expect(actual_states.sort).to eq(expected_states.sort), message || default_message
|
172
|
+
else
|
173
|
+
raise default_message unless expected_states.sort == actual_states.sort
|
174
|
+
end
|
175
|
+
end
|
176
|
+
|
177
|
+
def refute_sm_state_defined(machine, state, message = nil)
|
178
|
+
state_exists = machine.states.any? { |s| s.name == state }
|
179
|
+
default_message = "Expected state #{state} to not be defined in machine"
|
180
|
+
|
181
|
+
if defined?(::Minitest)
|
182
|
+
refute state_exists, message || default_message
|
183
|
+
elsif defined?(::RSpec)
|
184
|
+
expect(state_exists).to be_falsy, message || default_message
|
185
|
+
elsif state_exists
|
186
|
+
raise default_message
|
187
|
+
end
|
188
|
+
end
|
189
|
+
alias assert_sm_state_not_defined refute_sm_state_defined
|
190
|
+
|
191
|
+
def assert_sm_initial_state(machine, expected_state, message = nil)
|
192
|
+
state_obj = machine.state(expected_state)
|
193
|
+
is_initial = state_obj&.initial?
|
194
|
+
default_message = "Expected state #{expected_state} to be the initial state"
|
195
|
+
|
196
|
+
if defined?(::Minitest)
|
197
|
+
assert is_initial, message || default_message
|
198
|
+
elsif defined?(::RSpec)
|
199
|
+
expect(is_initial).to be_truthy, message || default_message
|
200
|
+
else
|
201
|
+
raise default_message unless is_initial
|
202
|
+
end
|
203
|
+
end
|
204
|
+
|
205
|
+
def assert_sm_final_state(machine, state, message = nil)
|
206
|
+
state_obj = machine.states[state]
|
207
|
+
is_final = state_obj&.final?
|
208
|
+
default_message = "Expected state #{state} to be final"
|
209
|
+
|
210
|
+
if defined?(::Minitest)
|
211
|
+
assert is_final, message || default_message
|
212
|
+
elsif defined?(::RSpec)
|
213
|
+
expect(is_final).to be_truthy, message || default_message
|
214
|
+
else
|
215
|
+
raise default_message unless is_final
|
216
|
+
end
|
217
|
+
end
|
218
|
+
|
219
|
+
def assert_sm_possible_transitions(machine, from:, expected_to_states:, message: nil)
|
220
|
+
actual_transitions = machine.events.flat_map do |event|
|
221
|
+
event.branches.select { |branch| branch.known_states.include?(from) }
|
222
|
+
.map(&:to)
|
223
|
+
end.uniq
|
224
|
+
default_message = "Expected transitions from #{from} to #{expected_to_states} but got #{actual_transitions}"
|
225
|
+
|
226
|
+
if defined?(::Minitest)
|
227
|
+
assert_equal expected_to_states.sort, actual_transitions.sort, message || default_message
|
228
|
+
elsif defined?(::RSpec)
|
229
|
+
expect(actual_transitions.sort).to eq(expected_to_states.sort), message || default_message
|
230
|
+
else
|
231
|
+
raise default_message unless expected_to_states.sort == actual_transitions.sort
|
232
|
+
end
|
233
|
+
end
|
234
|
+
|
235
|
+
def refute_sm_transition_allowed(machine, from:, to:, on:, message: nil)
|
236
|
+
event = machine.events[on]
|
237
|
+
is_allowed = event&.branches&.any? { |branch| branch.known_states.include?(from) && branch.to == to }
|
238
|
+
default_message = "Expected transition from #{from} to #{to} on #{on} to not be allowed"
|
239
|
+
|
240
|
+
if defined?(::Minitest)
|
241
|
+
refute is_allowed, message || default_message
|
242
|
+
elsif defined?(::RSpec)
|
243
|
+
expect(is_allowed).to be_falsy, message || default_message
|
244
|
+
elsif is_allowed
|
245
|
+
raise default_message
|
246
|
+
end
|
247
|
+
end
|
248
|
+
alias assert_sm_transition_not_allowed refute_sm_transition_allowed
|
249
|
+
|
250
|
+
def assert_sm_event_triggers(object, event, machine_name = :state, message = nil)
|
251
|
+
initial_state = object.send(machine_name)
|
252
|
+
object.send("#{event}!")
|
253
|
+
state_changed = initial_state != object.send(machine_name)
|
254
|
+
default_message = "Expected event #{event} to trigger state change on #{machine_name}"
|
255
|
+
|
256
|
+
if defined?(::Minitest)
|
257
|
+
assert state_changed, message || default_message
|
258
|
+
elsif defined?(::RSpec)
|
259
|
+
expect(state_changed).to be_truthy, message || default_message
|
260
|
+
else
|
261
|
+
raise default_message unless state_changed
|
262
|
+
end
|
263
|
+
end
|
264
|
+
|
265
|
+
def refute_sm_event_triggers(object, event, machine_name = :state, message = nil)
|
266
|
+
initial_state = object.send(machine_name)
|
267
|
+
begin
|
268
|
+
object.send("#{event}!")
|
269
|
+
state_unchanged = initial_state == object.send(machine_name)
|
270
|
+
default_message = "Expected event #{event} to not trigger state change on #{machine_name}"
|
271
|
+
|
272
|
+
if defined?(::Minitest)
|
273
|
+
assert state_unchanged, message || default_message
|
274
|
+
elsif defined?(::RSpec)
|
275
|
+
expect(state_unchanged).to be_truthy, message || default_message
|
276
|
+
else
|
277
|
+
raise default_message unless state_unchanged
|
278
|
+
end
|
279
|
+
rescue StateMachines::InvalidTransition
|
280
|
+
# Expected behavior - transition was blocked
|
281
|
+
end
|
282
|
+
end
|
283
|
+
alias assert_sm_event_not_triggers refute_sm_event_triggers
|
284
|
+
|
285
|
+
def assert_sm_event_raises_error(object, event, error_class, message = nil)
|
286
|
+
default_message = "Expected event #{event} to raise #{error_class}"
|
287
|
+
|
288
|
+
if defined?(::Minitest)
|
289
|
+
assert_raises(error_class, message || default_message) do
|
290
|
+
object.send("#{event}!")
|
291
|
+
end
|
292
|
+
elsif defined?(::RSpec)
|
293
|
+
expect { object.send("#{event}!") }.to raise_error(error_class), message || default_message
|
294
|
+
else
|
295
|
+
begin
|
296
|
+
object.send("#{event}!")
|
297
|
+
raise default_message
|
298
|
+
rescue error_class
|
299
|
+
# Expected behavior
|
300
|
+
end
|
301
|
+
end
|
302
|
+
end
|
303
|
+
|
304
|
+
def assert_sm_callback_executed(object, callback_name, message = nil)
|
305
|
+
callbacks_executed = object.instance_variable_get(:@_sm_callbacks_executed) || []
|
306
|
+
callback_was_executed = callbacks_executed.include?(callback_name)
|
307
|
+
default_message = "Expected callback #{callback_name} to be executed"
|
308
|
+
|
309
|
+
if defined?(::Minitest)
|
310
|
+
assert callback_was_executed, message || default_message
|
311
|
+
elsif defined?(::RSpec)
|
312
|
+
expect(callback_was_executed).to be_truthy, message || default_message
|
313
|
+
else
|
314
|
+
raise default_message unless callback_was_executed
|
315
|
+
end
|
316
|
+
end
|
317
|
+
|
318
|
+
def refute_sm_callback_executed(object, callback_name, message = nil)
|
319
|
+
callbacks_executed = object.instance_variable_get(:@_sm_callbacks_executed) || []
|
320
|
+
callback_was_executed = callbacks_executed.include?(callback_name)
|
321
|
+
default_message = "Expected callback #{callback_name} to not be executed"
|
322
|
+
|
323
|
+
if defined?(::Minitest)
|
324
|
+
refute callback_was_executed, message || default_message
|
325
|
+
elsif defined?(::RSpec)
|
326
|
+
expect(callback_was_executed).to be_falsy, message || default_message
|
327
|
+
elsif callback_was_executed
|
328
|
+
raise default_message
|
329
|
+
end
|
330
|
+
end
|
331
|
+
alias assert_sm_callback_not_executed refute_sm_callback_executed
|
332
|
+
|
333
|
+
# Assert that a record's state is persisted correctly for a specific state machine
|
334
|
+
#
|
335
|
+
# @param record [Object] The record to check (should respond to reload)
|
336
|
+
# @param expected [String, Symbol] The expected persisted state
|
337
|
+
# @param machine_name [Symbol] The name of the state machine (defaults to :state)
|
338
|
+
# @param message [String, nil] Custom failure message
|
339
|
+
# @return [void]
|
340
|
+
# @raise [AssertionError] If the persisted state doesn't match
|
341
|
+
#
|
342
|
+
# @example
|
343
|
+
# # Default state machine
|
344
|
+
# assert_sm_state_persisted(user, "active")
|
345
|
+
#
|
346
|
+
# # Specific state machine
|
347
|
+
# assert_sm_state_persisted(ship, "up", :shields)
|
348
|
+
# assert_sm_state_persisted(ship, "armed", :weapons)
|
349
|
+
def assert_sm_state_persisted(record, expected, machine_name = :state, message = nil)
|
350
|
+
record.reload if record.respond_to?(:reload)
|
351
|
+
actual_state = record.send(machine_name)
|
352
|
+
default_message = "Expected persisted state #{expected} for #{machine_name} but got #{actual_state}"
|
353
|
+
|
354
|
+
if defined?(::Minitest)
|
355
|
+
assert_equal expected, actual_state, message || default_message
|
356
|
+
elsif defined?(::RSpec)
|
357
|
+
expect(actual_state).to eq(expected), message || default_message
|
358
|
+
else
|
359
|
+
raise default_message unless expected == actual_state
|
360
|
+
end
|
361
|
+
end
|
362
|
+
|
363
|
+
# Assert that executing a block triggers one or more expected events
|
364
|
+
#
|
365
|
+
# @param object [Object] The object with state machines
|
366
|
+
# @param expected_events [Symbol, Array<Symbol>] The event(s) expected to be triggered
|
367
|
+
# @param machine_name [Symbol] The name of the state machine (defaults to :state)
|
368
|
+
# @param message [String, nil] Custom failure message
|
369
|
+
# @return [void]
|
370
|
+
# @raise [AssertionError] If the expected events were not triggered
|
371
|
+
#
|
372
|
+
# @example
|
373
|
+
# # Single event
|
374
|
+
# assert_sm_triggers_event(vehicle, :crash) { vehicle.redline }
|
375
|
+
#
|
376
|
+
# # Multiple events
|
377
|
+
# assert_sm_triggers_event(vehicle, [:crash, :emergency]) { vehicle.emergency_stop }
|
378
|
+
#
|
379
|
+
# # Specific machine
|
380
|
+
# assert_sm_triggers_event(vehicle, :disable, machine_name: :alarm) { vehicle.turn_off_alarm }
|
381
|
+
def assert_sm_triggers_event(object, expected_events, machine_name: :state, message: nil)
|
382
|
+
expected_events = Array(expected_events)
|
383
|
+
triggered_events = []
|
384
|
+
|
385
|
+
# Get the state machine
|
386
|
+
machine = object.class.state_machines[machine_name]
|
387
|
+
raise ArgumentError, "No state machine found for #{machine_name}" unless machine
|
388
|
+
|
389
|
+
# Save original callbacks to restore later
|
390
|
+
machine.callbacks[:before].dup
|
391
|
+
|
392
|
+
# Add a temporary callback to track triggered events
|
393
|
+
temp_callback = machine.before_transition do |_obj, transition|
|
394
|
+
triggered_events << transition.event if transition.event
|
395
|
+
end
|
396
|
+
|
397
|
+
begin
|
398
|
+
# Execute the block
|
399
|
+
yield
|
400
|
+
|
401
|
+
# Check if expected events were triggered
|
402
|
+
missing_events = expected_events - triggered_events
|
403
|
+
extra_events = triggered_events - expected_events
|
404
|
+
|
405
|
+
unless missing_events.empty? && extra_events.empty?
|
406
|
+
default_message = "Expected events #{expected_events.inspect} to be triggered, but got #{triggered_events.inspect}"
|
407
|
+
default_message += ". Missing: #{missing_events.inspect}" if missing_events.any?
|
408
|
+
default_message += ". Extra: #{extra_events.inspect}" if extra_events.any?
|
409
|
+
|
410
|
+
if defined?(::Minitest)
|
411
|
+
assert false, message || default_message
|
412
|
+
elsif defined?(::RSpec)
|
413
|
+
raise message || default_message
|
414
|
+
else
|
415
|
+
raise default_message
|
416
|
+
end
|
417
|
+
end
|
418
|
+
ensure
|
419
|
+
# Restore original callbacks by removing the temporary one
|
420
|
+
machine.callbacks[:before].delete(temp_callback)
|
421
|
+
end
|
422
|
+
end
|
423
|
+
|
424
|
+
# Assert that a before_transition callback is defined with expected arguments
|
425
|
+
#
|
426
|
+
# @param machine_or_class [StateMachines::Machine, Class] The machine or class to check
|
427
|
+
# @param options [Hash] Expected callback options (on:, from:, to:, do:, if:, unless:)
|
428
|
+
# @param message [String, nil] Custom failure message
|
429
|
+
# @return [void]
|
430
|
+
# @raise [AssertionError] If the callback is not defined
|
431
|
+
#
|
432
|
+
# @example
|
433
|
+
# # Check for specific transition callback
|
434
|
+
# assert_before_transition(Vehicle, on: :crash, do: :emergency_stop)
|
435
|
+
#
|
436
|
+
# # Check with from/to states
|
437
|
+
# assert_before_transition(Vehicle.state_machine, from: :parked, to: :idling, do: :start_engine)
|
438
|
+
#
|
439
|
+
# # Check with conditions
|
440
|
+
# assert_before_transition(Vehicle, on: :ignite, if: :seatbelt_on?)
|
441
|
+
def assert_before_transition(machine_or_class, options = {}, message = nil)
|
442
|
+
_assert_transition_callback(:before, machine_or_class, options, message)
|
443
|
+
end
|
444
|
+
|
445
|
+
# Assert that an after_transition callback is defined with expected arguments
|
446
|
+
#
|
447
|
+
# @param machine_or_class [StateMachines::Machine, Class] The machine or class to check
|
448
|
+
# @param options [Hash] Expected callback options (on:, from:, to:, do:, if:, unless:)
|
449
|
+
# @param message [String, nil] Custom failure message
|
450
|
+
# @return [void]
|
451
|
+
# @raise [AssertionError] If the callback is not defined
|
452
|
+
#
|
453
|
+
# @example
|
454
|
+
# # Check for specific transition callback
|
455
|
+
# assert_after_transition(Vehicle, on: :crash, do: :tow)
|
456
|
+
#
|
457
|
+
# # Check with from/to states
|
458
|
+
# assert_after_transition(Vehicle.state_machine, from: :stalled, to: :parked, do: :log_repair)
|
459
|
+
def assert_after_transition(machine_or_class, options = {}, message = nil)
|
460
|
+
_assert_transition_callback(:after, machine_or_class, options, message)
|
461
|
+
end
|
462
|
+
|
463
|
+
# === Sync Mode Assertions ===
|
464
|
+
|
465
|
+
# Assert that a state machine is operating in synchronous mode
|
466
|
+
#
|
467
|
+
# @param object [Object] The object with state machines
|
468
|
+
# @param machine_name [Symbol] The name of the state machine (defaults to :state)
|
469
|
+
# @param message [String, nil] Custom failure message
|
470
|
+
# @return [void]
|
471
|
+
# @raise [AssertionError] If the machine has async mode enabled
|
472
|
+
#
|
473
|
+
# @example
|
474
|
+
# user = User.new
|
475
|
+
# assert_sm_sync_mode(user) # Uses default :state machine
|
476
|
+
# assert_sm_sync_mode(user, :status) # Uses :status machine
|
477
|
+
def assert_sm_sync_mode(object, machine_name = :state, message = nil)
|
478
|
+
machine = object.class.state_machines[machine_name]
|
479
|
+
raise ArgumentError, "No state machine '#{machine_name}' found" unless machine
|
480
|
+
|
481
|
+
async_enabled = machine.respond_to?(:async_mode_enabled?) && machine.async_mode_enabled?
|
482
|
+
default_message = "Expected state machine '#{machine_name}' to be in sync mode, but async mode is enabled"
|
483
|
+
|
484
|
+
if defined?(::Minitest)
|
485
|
+
refute async_enabled, message || default_message
|
486
|
+
elsif defined?(::RSpec)
|
487
|
+
expect(async_enabled).to be_falsy, message || default_message
|
488
|
+
elsif async_enabled
|
489
|
+
raise default_message
|
490
|
+
end
|
491
|
+
end
|
492
|
+
|
493
|
+
# Assert that async methods are not available on a sync-only object
|
494
|
+
#
|
495
|
+
# @param object [Object] The object with state machines
|
496
|
+
# @param message [String, nil] Custom failure message
|
497
|
+
# @return [void]
|
498
|
+
# @raise [AssertionError] If async methods are available
|
499
|
+
#
|
500
|
+
# @example
|
501
|
+
# sync_only_car = Car.new # Car has no async: true machines
|
502
|
+
# assert_sm_no_async_methods(sync_only_car)
|
503
|
+
def assert_sm_no_async_methods(object, message = nil)
|
504
|
+
async_methods = %i[fire_event_async fire_events_async fire_event_async! async_fire_event]
|
505
|
+
available_async_methods = async_methods.select { |method| object.respond_to?(method) }
|
506
|
+
|
507
|
+
default_message = "Expected no async methods to be available, but found: #{available_async_methods.inspect}"
|
508
|
+
|
509
|
+
if defined?(::Minitest)
|
510
|
+
assert_empty available_async_methods, message || default_message
|
511
|
+
elsif defined?(::RSpec)
|
512
|
+
expect(available_async_methods).to be_empty, message || default_message
|
513
|
+
elsif available_async_methods.any?
|
514
|
+
raise default_message
|
515
|
+
end
|
516
|
+
end
|
517
|
+
|
518
|
+
# Assert that an object has no async-enabled state machines
|
519
|
+
#
|
520
|
+
# @param object [Object] The object with state machines
|
521
|
+
# @param message [String, nil] Custom failure message
|
522
|
+
# @return [void]
|
523
|
+
# @raise [AssertionError] If any machine has async mode enabled
|
524
|
+
#
|
525
|
+
# @example
|
526
|
+
# sync_only_vehicle = Vehicle.new # All machines are sync-only
|
527
|
+
# assert_sm_all_sync(sync_only_vehicle)
|
528
|
+
def assert_sm_all_sync(object, message = nil)
|
529
|
+
async_machines = []
|
530
|
+
|
531
|
+
object.class.state_machines.each do |name, machine|
|
532
|
+
if machine.respond_to?(:async_mode_enabled?) && machine.async_mode_enabled?
|
533
|
+
async_machines << name
|
534
|
+
end
|
535
|
+
end
|
536
|
+
|
537
|
+
default_message = "Expected all state machines to be sync-only, but these have async enabled: #{async_machines.inspect}"
|
538
|
+
|
539
|
+
if defined?(::Minitest)
|
540
|
+
assert_empty async_machines, message || default_message
|
541
|
+
elsif defined?(::RSpec)
|
542
|
+
expect(async_machines).to be_empty, message || default_message
|
543
|
+
elsif async_machines.any?
|
544
|
+
raise default_message
|
545
|
+
end
|
546
|
+
end
|
547
|
+
|
548
|
+
# Assert that synchronous event execution works correctly
|
549
|
+
#
|
550
|
+
# @param object [Object] The object with state machines
|
551
|
+
# @param event [Symbol] The event to trigger
|
552
|
+
# @param expected_state [Symbol] The expected state after transition
|
553
|
+
# @param machine_name [Symbol] The name of the state machine (defaults to :state)
|
554
|
+
# @param message [String, nil] Custom failure message
|
555
|
+
# @return [void]
|
556
|
+
# @raise [AssertionError] If sync execution fails
|
557
|
+
#
|
558
|
+
# @example
|
559
|
+
# car = Car.new
|
560
|
+
# assert_sm_sync_execution(car, :start, :running)
|
561
|
+
# assert_sm_sync_execution(car, :turn_on, :active, :alarm)
|
562
|
+
def assert_sm_sync_execution(object, event, expected_state, machine_name = :state, message = nil)
|
563
|
+
# Store initial state
|
564
|
+
initial_state = object.send(machine_name)
|
565
|
+
|
566
|
+
# Execute event synchronously
|
567
|
+
result = object.send("#{event}!")
|
568
|
+
|
569
|
+
# Verify immediate state change (no async delay)
|
570
|
+
final_state = object.send(machine_name)
|
571
|
+
|
572
|
+
# Check that transition succeeded
|
573
|
+
state_changed = initial_state != final_state
|
574
|
+
correct_final_state = final_state.to_s == expected_state.to_s
|
575
|
+
|
576
|
+
default_message = "Expected sync execution of '#{event}' to change #{machine_name} from '#{initial_state}' to '#{expected_state}', but got '#{final_state}'"
|
577
|
+
|
578
|
+
if defined?(::Minitest)
|
579
|
+
assert result, "Event #{event} should return true on success"
|
580
|
+
assert state_changed, "State should change from #{initial_state}"
|
581
|
+
assert correct_final_state, message || default_message
|
582
|
+
elsif defined?(::RSpec)
|
583
|
+
expect(result).to be_truthy, "Event #{event} should return true on success"
|
584
|
+
expect(state_changed).to be_truthy, "State should change from #{initial_state}"
|
585
|
+
expect(correct_final_state).to be_truthy, message || default_message
|
586
|
+
else
|
587
|
+
raise "Event #{event} should return true on success" unless result
|
588
|
+
raise "State should change from #{initial_state}" unless state_changed
|
589
|
+
raise default_message unless correct_final_state
|
590
|
+
end
|
591
|
+
end
|
592
|
+
|
593
|
+
# Assert that event execution is immediate (no async delay)
|
594
|
+
#
|
595
|
+
# @param object [Object] The object with state machines
|
596
|
+
# @param event [Symbol] The event to trigger
|
597
|
+
# @param machine_name [Symbol] The name of the state machine (defaults to :state)
|
598
|
+
# @param message [String, nil] Custom failure message
|
599
|
+
# @return [void]
|
600
|
+
# @raise [AssertionError] If execution appears to be async
|
601
|
+
#
|
602
|
+
# @example
|
603
|
+
# car = Car.new
|
604
|
+
# assert_sm_immediate_execution(car, :start)
|
605
|
+
def assert_sm_immediate_execution(object, event, machine_name = :state, message = nil)
|
606
|
+
initial_state = object.send(machine_name)
|
607
|
+
|
608
|
+
# Record start time and execute
|
609
|
+
start_time = Time.now
|
610
|
+
object.send("#{event}!")
|
611
|
+
execution_time = Time.now - start_time
|
612
|
+
|
613
|
+
final_state = object.send(machine_name)
|
614
|
+
state_changed = initial_state != final_state
|
615
|
+
|
616
|
+
# Should complete very quickly (under 10ms for sync operations)
|
617
|
+
is_immediate = execution_time < 0.01
|
618
|
+
|
619
|
+
default_message = "Expected immediate sync execution of '#{event}', but took #{execution_time}s (likely async)"
|
620
|
+
|
621
|
+
if defined?(::Minitest)
|
622
|
+
assert state_changed, "Event should trigger state change"
|
623
|
+
assert is_immediate, message || default_message
|
624
|
+
elsif defined?(::RSpec)
|
625
|
+
expect(state_changed).to be_truthy, "Event should trigger state change"
|
626
|
+
expect(is_immediate).to be_truthy, message || default_message
|
627
|
+
else
|
628
|
+
raise "Event should trigger state change" unless state_changed
|
629
|
+
raise default_message unless is_immediate
|
630
|
+
end
|
631
|
+
end
|
632
|
+
|
633
|
+
# === Async Mode Assertions ===
|
634
|
+
|
635
|
+
# Assert that a state machine is operating in asynchronous mode
|
636
|
+
#
|
637
|
+
# @param object [Object] The object with state machines
|
638
|
+
# @param machine_name [Symbol] The name of the state machine (defaults to :state)
|
639
|
+
# @param message [String, nil] Custom failure message
|
640
|
+
# @return [void]
|
641
|
+
# @raise [AssertionError] If the machine doesn't have async mode enabled
|
642
|
+
#
|
643
|
+
# @example
|
644
|
+
# drone = AutonomousDrone.new
|
645
|
+
# assert_sm_async_mode(drone) # Uses default :state machine
|
646
|
+
# assert_sm_async_mode(drone, :teleporter_status) # Uses :teleporter_status machine
|
647
|
+
def assert_sm_async_mode(object, machine_name = :state, message = nil)
|
648
|
+
machine = object.class.state_machines[machine_name]
|
649
|
+
raise ArgumentError, "No state machine '#{machine_name}' found" unless machine
|
650
|
+
|
651
|
+
async_enabled = machine.respond_to?(:async_mode_enabled?) && machine.async_mode_enabled?
|
652
|
+
default_message = "Expected state machine '#{machine_name}' to have async mode enabled, but it's in sync mode"
|
653
|
+
|
654
|
+
if defined?(::Minitest)
|
655
|
+
assert async_enabled, message || default_message
|
656
|
+
elsif defined?(::RSpec)
|
657
|
+
expect(async_enabled).to be_truthy, message || default_message
|
658
|
+
else
|
659
|
+
raise default_message unless async_enabled
|
660
|
+
end
|
661
|
+
end
|
662
|
+
|
663
|
+
# Assert that async methods are available on an async-enabled object
|
664
|
+
#
|
665
|
+
# @param object [Object] The object with state machines
|
666
|
+
# @param message [String, nil] Custom failure message
|
667
|
+
# @return [void]
|
668
|
+
# @raise [AssertionError] If async methods are not available
|
669
|
+
#
|
670
|
+
# @example
|
671
|
+
# drone = AutonomousDrone.new # Has async: true machines
|
672
|
+
# assert_sm_async_methods(drone)
|
673
|
+
def assert_sm_async_methods(object, message = nil)
|
674
|
+
async_methods = %i[fire_event_async fire_events_async fire_event_async! async_fire_event]
|
675
|
+
available_async_methods = async_methods.select { |method| object.respond_to?(method) }
|
676
|
+
|
677
|
+
default_message = "Expected async methods to be available, but found none"
|
678
|
+
|
679
|
+
if defined?(::Minitest)
|
680
|
+
refute_empty available_async_methods, message || default_message
|
681
|
+
elsif defined?(::RSpec)
|
682
|
+
expect(available_async_methods).not_to be_empty, message || default_message
|
683
|
+
elsif available_async_methods.empty?
|
684
|
+
raise default_message
|
685
|
+
end
|
686
|
+
end
|
687
|
+
|
688
|
+
# Assert that an object has async-enabled state machines
|
689
|
+
#
|
690
|
+
# @param object [Object] The object with state machines
|
691
|
+
# @param machine_names [Array<Symbol>] Expected async machine names
|
692
|
+
# @param message [String, nil] Custom failure message
|
693
|
+
# @return [void]
|
694
|
+
# @raise [AssertionError] If expected machines don't have async mode
|
695
|
+
#
|
696
|
+
# @example
|
697
|
+
# drone = AutonomousDrone.new
|
698
|
+
# assert_sm_has_async(drone, [:status, :teleporter_status, :shields])
|
699
|
+
def assert_sm_has_async(object, machine_names = nil, message = nil)
|
700
|
+
if machine_names
|
701
|
+
# Check specific machines
|
702
|
+
non_async_machines = machine_names.reject do |name|
|
703
|
+
machine = object.class.state_machines[name]
|
704
|
+
machine&.respond_to?(:async_mode_enabled?) && machine.async_mode_enabled?
|
705
|
+
end
|
706
|
+
|
707
|
+
default_message = "Expected machines #{machine_names.inspect} to have async enabled, but these don't: #{non_async_machines.inspect}"
|
708
|
+
|
709
|
+
if defined?(::Minitest)
|
710
|
+
assert_empty non_async_machines, message || default_message
|
711
|
+
elsif defined?(::RSpec)
|
712
|
+
expect(non_async_machines).to be_empty, message || default_message
|
713
|
+
elsif non_async_machines.any?
|
714
|
+
raise default_message
|
715
|
+
end
|
716
|
+
else
|
717
|
+
# Check that at least one machine has async
|
718
|
+
async_machines = object.class.state_machines.select do |name, machine|
|
719
|
+
machine.respond_to?(:async_mode_enabled?) && machine.async_mode_enabled?
|
720
|
+
end
|
721
|
+
|
722
|
+
default_message = "Expected at least one state machine to have async enabled, but none found"
|
723
|
+
|
724
|
+
if defined?(::Minitest)
|
725
|
+
refute_empty async_machines, message || default_message
|
726
|
+
elsif defined?(::RSpec)
|
727
|
+
expect(async_machines).not_to be_empty, message || default_message
|
728
|
+
elsif async_machines.empty?
|
729
|
+
raise default_message
|
730
|
+
end
|
731
|
+
end
|
732
|
+
end
|
733
|
+
|
734
|
+
# Assert that individual async event methods are available
|
735
|
+
#
|
736
|
+
# @param object [Object] The object with state machines
|
737
|
+
# @param event [Symbol] The event name
|
738
|
+
# @param message [String, nil] Custom failure message
|
739
|
+
# @return [void]
|
740
|
+
# @raise [AssertionError] If async event methods are not available
|
741
|
+
#
|
742
|
+
# @example
|
743
|
+
# drone = AutonomousDrone.new
|
744
|
+
# assert_sm_async_event_methods(drone, :launch) # Checks launch_async and launch_async!
|
745
|
+
def assert_sm_async_event_methods(object, event, message = nil)
|
746
|
+
async_method = "#{event}_async".to_sym
|
747
|
+
async_bang_method = "#{event}_async!".to_sym
|
748
|
+
|
749
|
+
has_async = object.respond_to?(async_method)
|
750
|
+
has_async_bang = object.respond_to?(async_bang_method)
|
751
|
+
|
752
|
+
default_message = "Expected async event methods #{async_method} and #{async_bang_method} to be available for event :#{event}"
|
753
|
+
|
754
|
+
if defined?(::Minitest)
|
755
|
+
assert has_async, "Missing #{async_method} method"
|
756
|
+
assert has_async_bang, "Missing #{async_bang_method} method"
|
757
|
+
elsif defined?(::RSpec)
|
758
|
+
expect(has_async).to be_truthy, "Missing #{async_method} method"
|
759
|
+
expect(has_async_bang).to be_truthy, "Missing #{async_bang_method} method"
|
760
|
+
else
|
761
|
+
raise "Missing #{async_method} method" unless has_async
|
762
|
+
raise "Missing #{async_bang_method} method" unless has_async_bang
|
763
|
+
end
|
764
|
+
end
|
765
|
+
|
766
|
+
# Assert that an object has thread-safe state methods when async is enabled
|
767
|
+
#
|
768
|
+
# @param object [Object] The object with state machines
|
769
|
+
# @param message [String, nil] Custom failure message
|
770
|
+
# @return [void]
|
771
|
+
# @raise [AssertionError] If thread-safe methods are not available
|
772
|
+
#
|
773
|
+
# @example
|
774
|
+
# drone = AutonomousDrone.new
|
775
|
+
# assert_sm_thread_safe_methods(drone)
|
776
|
+
def assert_sm_thread_safe_methods(object, message = nil)
|
777
|
+
thread_safe_methods = %i[state_machine_mutex read_state_safely write_state_safely]
|
778
|
+
missing_methods = thread_safe_methods.reject { |method| object.respond_to?(method) }
|
779
|
+
|
780
|
+
default_message = "Expected thread-safe methods to be available, but missing: #{missing_methods.inspect}"
|
781
|
+
|
782
|
+
if defined?(::Minitest)
|
783
|
+
assert_empty missing_methods, message || default_message
|
784
|
+
elsif defined?(::RSpec)
|
785
|
+
expect(missing_methods).to be_empty, message || default_message
|
786
|
+
elsif missing_methods.any?
|
787
|
+
raise default_message
|
788
|
+
end
|
789
|
+
end
|
790
|
+
|
791
|
+
# RSpec-style aliases for event triggering (for consistency with RSpec expectations)
|
792
|
+
alias expect_to_trigger_event assert_sm_triggers_event
|
793
|
+
alias have_triggered_event assert_sm_triggers_event
|
794
|
+
|
795
|
+
private
|
796
|
+
|
797
|
+
# Internal helper for checking transition callbacks
|
798
|
+
def _assert_transition_callback(callback_type, machine_or_class, options, message)
|
799
|
+
# Get the machine
|
800
|
+
machine = machine_or_class.is_a?(StateMachines::Machine) ? machine_or_class : machine_or_class.state_machine
|
801
|
+
raise ArgumentError, 'No state machine found' unless machine
|
802
|
+
|
803
|
+
callbacks = machine.callbacks[callback_type] || []
|
804
|
+
|
805
|
+
# Extract expected conditions
|
806
|
+
expected_event = options[:on]
|
807
|
+
expected_from = options[:from]
|
808
|
+
expected_to = options[:to]
|
809
|
+
expected_method = options[:do]
|
810
|
+
expected_if = options[:if]
|
811
|
+
expected_unless = options[:unless]
|
812
|
+
|
813
|
+
# Find matching callback
|
814
|
+
matching_callback = callbacks.find do |callback|
|
815
|
+
branch = callback.branch
|
816
|
+
|
817
|
+
# Check event requirement
|
818
|
+
if expected_event
|
819
|
+
event_requirement = branch.event_requirement
|
820
|
+
event_matches = if event_requirement && event_requirement.respond_to?(:values)
|
821
|
+
event_requirement.values.include?(expected_event)
|
822
|
+
else
|
823
|
+
false
|
824
|
+
end
|
825
|
+
next false unless event_matches
|
826
|
+
end
|
827
|
+
|
828
|
+
# Check state requirements (from/to)
|
829
|
+
if expected_from || expected_to
|
830
|
+
state_matches = false
|
831
|
+
branch.state_requirements.each do |req|
|
832
|
+
from_matches = !expected_from || (req[:from] && req[:from].respond_to?(:values) && req[:from].values.include?(expected_from))
|
833
|
+
to_matches = !expected_to || (req[:to] && req[:to].respond_to?(:values) && req[:to].values.include?(expected_to))
|
834
|
+
|
835
|
+
if from_matches && to_matches
|
836
|
+
state_matches = true
|
837
|
+
break
|
838
|
+
end
|
839
|
+
end
|
840
|
+
next false unless state_matches
|
841
|
+
end
|
842
|
+
|
843
|
+
# Check method requirement
|
844
|
+
if expected_method
|
845
|
+
methods = callback.instance_variable_get(:@methods) || []
|
846
|
+
method_matches = methods.any? do |method|
|
847
|
+
(method.is_a?(Symbol) && method == expected_method) ||
|
848
|
+
(method.is_a?(String) && method.to_sym == expected_method) ||
|
849
|
+
(method.respond_to?(:call) && method.respond_to?(:source_location))
|
850
|
+
end
|
851
|
+
next false unless method_matches
|
852
|
+
end
|
853
|
+
|
854
|
+
# Check if condition
|
855
|
+
if expected_if
|
856
|
+
if_condition = branch.if_condition
|
857
|
+
if_matches = (if_condition.is_a?(Symbol) && if_condition == expected_if) ||
|
858
|
+
(if_condition.is_a?(String) && if_condition.to_sym == expected_if) ||
|
859
|
+
if_condition.respond_to?(:call)
|
860
|
+
next false unless if_matches
|
861
|
+
end
|
862
|
+
|
863
|
+
# Check unless condition
|
864
|
+
if expected_unless
|
865
|
+
unless_condition = branch.unless_condition
|
866
|
+
unless_matches = (unless_condition.is_a?(Symbol) && unless_condition == expected_unless) ||
|
867
|
+
(unless_condition.is_a?(String) && unless_condition.to_sym == expected_unless) ||
|
868
|
+
unless_condition.respond_to?(:call)
|
869
|
+
next false unless unless_matches
|
870
|
+
end
|
871
|
+
|
872
|
+
true
|
873
|
+
end
|
874
|
+
|
875
|
+
return if matching_callback
|
876
|
+
|
877
|
+
expected_parts = []
|
878
|
+
expected_parts << "on: #{expected_event.inspect}" if expected_event
|
879
|
+
expected_parts << "from: #{expected_from.inspect}" if expected_from
|
880
|
+
expected_parts << "to: #{expected_to.inspect}" if expected_to
|
881
|
+
expected_parts << "do: #{expected_method.inspect}" if expected_method
|
882
|
+
expected_parts << "if: #{expected_if.inspect}" if expected_if
|
883
|
+
expected_parts << "unless: #{expected_unless.inspect}" if expected_unless
|
884
|
+
|
885
|
+
default_message = "Expected #{callback_type}_transition callback with #{expected_parts.join(', ')} to be defined, but it was not found"
|
886
|
+
|
887
|
+
if defined?(::Minitest)
|
888
|
+
assert false, message || default_message
|
889
|
+
elsif defined?(::RSpec)
|
890
|
+
raise message || default_message
|
891
|
+
else
|
892
|
+
raise default_message
|
893
|
+
end
|
894
|
+
end
|
895
|
+
end
|
896
|
+
end
|