diff options
-rw-r--r-- | spec/ruby/.rubocop.yml | 2 | ||||
-rw-r--r-- | spec/ruby/core/array/fixtures/classes.rb | 744 | ||||
-rw-r--r-- | spec/ruby/core/array/pack/buffer_spec.rb | 10 | ||||
-rw-r--r-- | spec/ruby/fixtures/io.rb (renamed from spec/ruby/library/io-wait/fixtures/classes.rb) | 10 | ||||
-rw-r--r-- | spec/ruby/language/case_spec.rb | 2 | ||||
-rw-r--r-- | spec/ruby/language/pattern_matching/3.1.rb | 75 | ||||
-rw-r--r-- | spec/ruby/language/pattern_matching_spec.rb | 1468 | ||||
-rw-r--r-- | spec/ruby/library/io-wait/wait_readable_spec.rb | 19 | ||||
-rw-r--r-- | spec/ruby/library/io-wait/wait_spec.rb | 39 | ||||
-rw-r--r-- | spec/ruby/library/io-wait/wait_writable_spec.rb | 21 | ||||
-rw-r--r-- | spec/ruby/library/rbconfig/rbconfig_spec.rb | 53 | ||||
-rw-r--r-- | spec/ruby/optional/capi/io_spec.rb | 63 |
12 files changed, 1238 insertions, 1268 deletions
diff --git a/spec/ruby/.rubocop.yml b/spec/ruby/.rubocop.yml index be32ce8900..5a902e0f01 100644 --- a/spec/ruby/.rubocop.yml +++ b/spec/ruby/.rubocop.yml @@ -1,7 +1,7 @@ inherit_from: .rubocop_todo.yml AllCops: - TargetRubyVersion: 3.0 + TargetRubyVersion: 3.1 DisplayCopNames: true Exclude: - command_line/fixtures/bad_syntax.rb diff --git a/spec/ruby/core/array/fixtures/classes.rb b/spec/ruby/core/array/fixtures/classes.rb index 8596245fb8..05283c0f74 100644 --- a/spec/ruby/core/array/fixtures/classes.rb +++ b/spec/ruby/core/array/fixtures/classes.rb @@ -56,23 +56,20 @@ module ArraySpecs 101.621, 102.816, 104.010, 105.202, 106.393, 107.583, 108.771, 109.958, 111.144, 112.329, 113.512, 114.695, 115.876, 117.057, 118.236, 119.414, 120.591, 121.767, 122.942, 124.116, 125.289, 126.462, 127.633, 128.803, 129.973, 131.141, 132.309, 133.476, 134.642, 135.807, - ] + ] def self.measure_sample_fairness(size, samples, iters) ary = Array.new(size) { |x| x } + expected = iters.fdiv size (samples).times do |i| chi_results = [] 3.times do - counts = Array.new(size) { 0 } - expected = iters / size + counts = Array.new(size, 0) iters.times do x = ary.sample(samples)[i] counts[x] += 1 end - chi_squared = 0.0 - counts.each do |count| - chi_squared += (((count - expected) ** 2) * 1.0 / expected) - end + chi_squared = counts.sum {|count| (count - expected) ** 2} / expected chi_results << chi_squared break if chi_squared <= CHI_SQUARED_CRITICAL_VALUES[size] end @@ -83,17 +80,14 @@ module ArraySpecs def self.measure_sample_fairness_large_sample_size(size, samples, iters) ary = Array.new(size) { |x| x } - counts = Array.new(size) { 0 } - expected = iters * samples / size + counts = Array.new(size, 0) + expected = (iters * samples).fdiv size iters.times do ary.sample(samples).each do |sample| counts[sample] += 1 end end - chi_squared = 0.0 - counts.each do |count| - chi_squared += (((count - expected) ** 2) * 1.0 / expected) - end + chi_squared = counts.sum {|count| (count - expected) ** 2} / expected # Chi squared critical values for tests with 4 degrees of freedom # Values obtained from NIST Engineering Statistic Handbook at @@ -223,366 +217,370 @@ module ArraySpecs obj end - LargeArray = ["test_create_table_with_force_true_does_not_drop_nonexisting_table", - "test_add_table", - "assert_difference", - "assert_operator", - "instance_variables", - "class", - "instance_variable_get", - "__class__", - "expects", - "assert_no_difference", - "name", - "assert_blank", - "assert_not_same", - "is_a?", - "test_add_table_with_decimals", - "test_create_table_with_timestamps_should_create_datetime_columns", - "assert_present", - "assert_no_match", - "__instance_of__", - "assert_deprecated", - "assert", - "assert_throws", - "kind_of?", - "try", - "__instance_variable_get__", - "object_id", - "timeout", - "instance_variable_set", - "assert_nothing_thrown", - "__instance_variable_set__", - "copy_object", - "test_create_table_with_timestamps_should_create_datetime_columns_with_options", - "assert_not_deprecated", - "assert_in_delta", - "id", - "copy_metaclass", - "test_create_table_without_a_block", - "dup", - "assert_not_nil", - "send", - "__instance_variables__", - "to_sql", - "mock", - "assert_send", - "instance_variable_defined?", - "clone", - "require", - "test_migrator", - "__instance_variable_defined_eh__", - "frozen?", - "test_add_column_not_null_with_default", - "freeze", - "test_migrator_one_up", - "test_migrator_one_down", - "singleton_methods", - "method_exists?", - "create_fixtures", - "test_migrator_one_up_one_down", - "test_native_decimal_insert_manual_vs_automatic", - "instance_exec", - "__is_a__", - "test_migrator_double_up", - "stub", - "private_methods", - "stubs", - "test_migrator_double_down", - "fixture_path", - "private_singleton_methods", - "stub_everything", - "test_migrator_one_up_with_exception_and_rollback", - "sequence", - "protected_methods", - "enum_for", - "test_finds_migrations", - "run_before_mocha", - "states", - "protected_singleton_methods", - "to_json", - "instance_values", - "==", - "mocha_setup", - "public_methods", - "test_finds_pending_migrations", - "mocha_verify", - "assert_kind_of", - "===", - "=~", - "test_relative_migrations", - "mocha_teardown", - "gem", - "mocha", - "test_only_loads_pending_migrations", - "test_add_column_with_precision_and_scale", - "require_or_load", - "eql?", - "require_dependency", - "test_native_types", - "test_target_version_zero_should_run_only_once", - "extend", - "to_matcher", - "unloadable", - "require_association", - "hash", - "__id__", - "load_dependency", - "equals", - "test_migrator_db_has_no_schema_migrations_table", - "test_migrator_verbosity", - "kind_of", - "to_yaml", - "to_bool", - "test_migrator_verbosity_off", - "taint", - "test_migrator_going_down_due_to_version_target", - "tainted?", - "mocha_inspect", - "test_migrator_rollback", - "vim", - "untaint", - "taguri=", - "test_migrator_forward", - "test_schema_migrations_table_name", - "test_proper_table_name", - "all_of", - "test_add_drop_table_with_prefix_and_suffix", - "_setup_callbacks", - "setup", - "Not", - "test_create_table_with_binary_column", - "assert_not_equal", - "enable_warnings", - "acts_like?", - "Rational", - "_removed_setup_callbacks", - "Table", - "bind", - "any_of", - "__method__", - "test_migrator_with_duplicates", - "_teardown_callbacks", - "method", - "test_migrator_with_duplicate_names", - "_removed_teardown_callbacks", - "any_parameters", - "test_migrator_with_missing_version_numbers", - "test_add_remove_single_field_using_string_arguments", - "test_create_table_with_custom_sequence_name", - "test_add_remove_single_field_using_symbol_arguments", - "_one_time_conditions_valid_14?", - "_one_time_conditions_valid_16?", - "run_callbacks", - "anything", - "silence_warnings", - "instance_variable_names", - "_fixture_path", - "copy_instance_variables_from", - "fixture_path?", - "has_entry", - "__marshal__", - "_fixture_table_names", - "__kind_of__", - "fixture_table_names?", - "test_add_rename", - "assert_equal", - "_fixture_class_names", - "fixture_class_names?", - "has_entries", - "_use_transactional_fixtures", - "people", - "test_rename_column_using_symbol_arguments", - "use_transactional_fixtures?", - "instance_eval", - "blank?", - "with_warnings", - "__nil__", - "load", - "metaclass", - "_use_instantiated_fixtures", - "has_key", - "class_eval", - "present?", - "test_rename_column", - "teardown", - "use_instantiated_fixtures?", - "method_name", - "silence_stderr", - "presence", - "test_rename_column_preserves_default_value_not_null", - "silence_stream", - "_pre_loaded_fixtures", - "__metaclass__", - "__fixnum__", - "pre_loaded_fixtures?", - "has_value", - "suppress", - "to_yaml_properties", - "test_rename_nonexistent_column", - "test_add_index", - "includes", - "find_correlate_in", - "equality_predicate_sql", - "assert_nothing_raised", - "let", - "not_predicate_sql", - "test_rename_column_with_sql_reserved_word", - "singleton_class", - "test_rename_column_with_an_index", - "display", - "taguri", - "to_yaml_style", - "test_remove_column_with_index", - "size", - "current_adapter?", - "test_remove_column_with_multi_column_index", - "respond_to?", - "test_change_type_of_not_null_column", - "is_a", - "to_a", - "test_rename_table_for_sqlite_should_work_with_reserved_words", - "require_library_or_gem", - "setup_fixtures", - "equal?", - "teardown_fixtures", - "nil?", - "fixture_table_names", - "fixture_class_names", - "test_create_table_without_id", - "use_transactional_fixtures", - "test_add_column_with_primary_key_attribute", - "repair_validations", - "use_instantiated_fixtures", - "instance_of?", - "test_create_table_adds_id", - "test_rename_table", - "pre_loaded_fixtures", - "to_enum", - "test_create_table_with_not_null_column", - "instance_of", - "test_change_column_nullability", - "optionally", - "test_rename_table_with_an_index", - "run", - "test_change_column", - "default_test", - "assert_raise", - "test_create_table_with_defaults", - "assert_nil", - "flunk", - "regexp_matches", - "duplicable?", - "reset_mocha", - "stubba_method", - "filter_backtrace", - "test_create_table_with_limits", - "responds_with", - "stubba_object", - "test_change_column_with_nil_default", - "assert_block", - "__show__", - "assert_date_from_db", - "__respond_to_eh__", - "run_in_transaction?", - "inspect", - "assert_sql", - "test_change_column_with_new_default", - "yaml_equivalent", - "build_message", - "to_s", - "test_change_column_default", - "assert_queries", - "pending", - "as_json", - "assert_no_queries", - "test_change_column_quotes_column_names", - "assert_match", - "test_keeping_default_and_notnull_constraint_on_change", - "methods", - "connection_allow_concurrency_setup", - "connection_allow_concurrency_teardown", - "test_create_table_with_primary_key_prefix_as_table_name_with_underscore", - "__send__", - "make_connection", - "assert_raises", - "tap", - "with_kcode", - "assert_instance_of", - "test_create_table_with_primary_key_prefix_as_table_name", - "assert_respond_to", - "test_change_column_default_to_null", - "assert_same", - "__extend__"] - - LargeTestArraySorted = ["test_add_column_not_null_with_default", - "test_add_column_with_precision_and_scale", - "test_add_column_with_primary_key_attribute", - "test_add_drop_table_with_prefix_and_suffix", - "test_add_index", - "test_add_remove_single_field_using_string_arguments", - "test_add_remove_single_field_using_symbol_arguments", - "test_add_rename", - "test_add_table", - "test_add_table_with_decimals", - "test_change_column", - "test_change_column_default", - "test_change_column_default_to_null", - "test_change_column_nullability", - "test_change_column_quotes_column_names", - "test_change_column_with_new_default", - "test_change_column_with_nil_default", - "test_change_type_of_not_null_column", - "test_create_table_adds_id", - "test_create_table_with_binary_column", - "test_create_table_with_custom_sequence_name", - "test_create_table_with_defaults", - "test_create_table_with_force_true_does_not_drop_nonexisting_table", - "test_create_table_with_limits", - "test_create_table_with_not_null_column", - "test_create_table_with_primary_key_prefix_as_table_name", - "test_create_table_with_primary_key_prefix_as_table_name_with_underscore", - "test_create_table_with_timestamps_should_create_datetime_columns", - "test_create_table_with_timestamps_should_create_datetime_columns_with_options", - "test_create_table_without_a_block", - "test_create_table_without_id", - "test_finds_migrations", - "test_finds_pending_migrations", - "test_keeping_default_and_notnull_constraint_on_change", - "test_migrator", - "test_migrator_db_has_no_schema_migrations_table", - "test_migrator_double_down", - "test_migrator_double_up", - "test_migrator_forward", - "test_migrator_going_down_due_to_version_target", - "test_migrator_one_down", - "test_migrator_one_up", - "test_migrator_one_up_one_down", - "test_migrator_one_up_with_exception_and_rollback", - "test_migrator_rollback", - "test_migrator_verbosity", - "test_migrator_verbosity_off", - "test_migrator_with_duplicate_names", - "test_migrator_with_duplicates", - "test_migrator_with_missing_version_numbers", - "test_native_decimal_insert_manual_vs_automatic", - "test_native_types", - "test_only_loads_pending_migrations", - "test_proper_table_name", - "test_relative_migrations", - "test_remove_column_with_index", - "test_remove_column_with_multi_column_index", - "test_rename_column", - "test_rename_column_preserves_default_value_not_null", - "test_rename_column_using_symbol_arguments", - "test_rename_column_with_an_index", - "test_rename_column_with_sql_reserved_word", - "test_rename_nonexistent_column", - "test_rename_table", - "test_rename_table_for_sqlite_should_work_with_reserved_words", - "test_rename_table_with_an_index", - "test_schema_migrations_table_name", - "test_target_version_zero_should_run_only_once"] + LargeArray = [ + "test_create_table_with_force_true_does_not_drop_nonexisting_table", + "test_add_table", + "assert_difference", + "assert_operator", + "instance_variables", + "class", + "instance_variable_get", + "__class__", + "expects", + "assert_no_difference", + "name", + "assert_blank", + "assert_not_same", + "is_a?", + "test_add_table_with_decimals", + "test_create_table_with_timestamps_should_create_datetime_columns", + "assert_present", + "assert_no_match", + "__instance_of__", + "assert_deprecated", + "assert", + "assert_throws", + "kind_of?", + "try", + "__instance_variable_get__", + "object_id", + "timeout", + "instance_variable_set", + "assert_nothing_thrown", + "__instance_variable_set__", + "copy_object", + "test_create_table_with_timestamps_should_create_datetime_columns_with_options", + "assert_not_deprecated", + "assert_in_delta", + "id", + "copy_metaclass", + "test_create_table_without_a_block", + "dup", + "assert_not_nil", + "send", + "__instance_variables__", + "to_sql", + "mock", + "assert_send", + "instance_variable_defined?", + "clone", + "require", + "test_migrator", + "__instance_variable_defined_eh__", + "frozen?", + "test_add_column_not_null_with_default", + "freeze", + "test_migrator_one_up", + "test_migrator_one_down", + "singleton_methods", + "method_exists?", + "create_fixtures", + "test_migrator_one_up_one_down", + "test_native_decimal_insert_manual_vs_automatic", + "instance_exec", + "__is_a__", + "test_migrator_double_up", + "stub", + "private_methods", + "stubs", + "test_migrator_double_down", + "fixture_path", + "private_singleton_methods", + "stub_everything", + "test_migrator_one_up_with_exception_and_rollback", + "sequence", + "protected_methods", + "enum_for", + "test_finds_migrations", + "run_before_mocha", + "states", + "protected_singleton_methods", + "to_json", + "instance_values", + "==", + "mocha_setup", + "public_methods", + "test_finds_pending_migrations", + "mocha_verify", + "assert_kind_of", + "===", + "=~", + "test_relative_migrations", + "mocha_teardown", + "gem", + "mocha", + "test_only_loads_pending_migrations", + "test_add_column_with_precision_and_scale", + "require_or_load", + "eql?", + "require_dependency", + "test_native_types", + "test_target_version_zero_should_run_only_once", + "extend", + "to_matcher", + "unloadable", + "require_association", + "hash", + "__id__", + "load_dependency", + "equals", + "test_migrator_db_has_no_schema_migrations_table", + "test_migrator_verbosity", + "kind_of", + "to_yaml", + "to_bool", + "test_migrator_verbosity_off", + "taint", + "test_migrator_going_down_due_to_version_target", + "tainted?", + "mocha_inspect", + "test_migrator_rollback", + "vim", + "untaint", + "taguri=", + "test_migrator_forward", + "test_schema_migrations_table_name", + "test_proper_table_name", + "all_of", + "test_add_drop_table_with_prefix_and_suffix", + "_setup_callbacks", + "setup", + "Not", + "test_create_table_with_binary_column", + "assert_not_equal", + "enable_warnings", + "acts_like?", + "Rational", + "_removed_setup_callbacks", + "Table", + "bind", + "any_of", + "__method__", + "test_migrator_with_duplicates", + "_teardown_callbacks", + "method", + "test_migrator_with_duplicate_names", + "_removed_teardown_callbacks", + "any_parameters", + "test_migrator_with_missing_version_numbers", + "test_add_remove_single_field_using_string_arguments", + "test_create_table_with_custom_sequence_name", + "test_add_remove_single_field_using_symbol_arguments", + "_one_time_conditions_valid_14?", + "_one_time_conditions_valid_16?", + "run_callbacks", + "anything", + "silence_warnings", + "instance_variable_names", + "_fixture_path", + "copy_instance_variables_from", + "fixture_path?", + "has_entry", + "__marshal__", + "_fixture_table_names", + "__kind_of__", + "fixture_table_names?", + "test_add_rename", + "assert_equal", + "_fixture_class_names", + "fixture_class_names?", + "has_entries", + "_use_transactional_fixtures", + "people", + "test_rename_column_using_symbol_arguments", + "use_transactional_fixtures?", + "instance_eval", + "blank?", + "with_warnings", + "__nil__", + "load", + "metaclass", + "_use_instantiated_fixtures", + "has_key", + "class_eval", + "present?", + "test_rename_column", + "teardown", + "use_instantiated_fixtures?", + "method_name", + "silence_stderr", + "presence", + "test_rename_column_preserves_default_value_not_null", + "silence_stream", + "_pre_loaded_fixtures", + "__metaclass__", + "__fixnum__", + "pre_loaded_fixtures?", + "has_value", + "suppress", + "to_yaml_properties", + "test_rename_nonexistent_column", + "test_add_index", + "includes", + "find_correlate_in", + "equality_predicate_sql", + "assert_nothing_raised", + "let", + "not_predicate_sql", + "test_rename_column_with_sql_reserved_word", + "singleton_class", + "test_rename_column_with_an_index", + "display", + "taguri", + "to_yaml_style", + "test_remove_column_with_index", + "size", + "current_adapter?", + "test_remove_column_with_multi_column_index", + "respond_to?", + "test_change_type_of_not_null_column", + "is_a", + "to_a", + "test_rename_table_for_sqlite_should_work_with_reserved_words", + "require_library_or_gem", + "setup_fixtures", + "equal?", + "teardown_fixtures", + "nil?", + "fixture_table_names", + "fixture_class_names", + "test_create_table_without_id", + "use_transactional_fixtures", + "test_add_column_with_primary_key_attribute", + "repair_validations", + "use_instantiated_fixtures", + "instance_of?", + "test_create_table_adds_id", + "test_rename_table", + "pre_loaded_fixtures", + "to_enum", + "test_create_table_with_not_null_column", + "instance_of", + "test_change_column_nullability", + "optionally", + "test_rename_table_with_an_index", + "run", + "test_change_column", + "default_test", + "assert_raise", + "test_create_table_with_defaults", + "assert_nil", + "flunk", + "regexp_matches", + "duplicable?", + "reset_mocha", + "stubba_method", + "filter_backtrace", + "test_create_table_with_limits", + "responds_with", + "stubba_object", + "test_change_column_with_nil_default", + "assert_block", + "__show__", + "assert_date_from_db", + "__respond_to_eh__", + "run_in_transaction?", + "inspect", + "assert_sql", + "test_change_column_with_new_default", + "yaml_equivalent", + "build_message", + "to_s", + "test_change_column_default", + "assert_queries", + "pending", + "as_json", + "assert_no_queries", + "test_change_column_quotes_column_names", + "assert_match", + "test_keeping_default_and_notnull_constraint_on_change", + "methods", + "connection_allow_concurrency_setup", + "connection_allow_concurrency_teardown", + "test_create_table_with_primary_key_prefix_as_table_name_with_underscore", + "__send__", + "make_connection", + "assert_raises", + "tap", + "with_kcode", + "assert_instance_of", + "test_create_table_with_primary_key_prefix_as_table_name", + "assert_respond_to", + "test_change_column_default_to_null", + "assert_same", + "__extend__", + ] + + LargeTestArraySorted = [ + "test_add_column_not_null_with_default", + "test_add_column_with_precision_and_scale", + "test_add_column_with_primary_key_attribute", + "test_add_drop_table_with_prefix_and_suffix", + "test_add_index", + "test_add_remove_single_field_using_string_arguments", + "test_add_remove_single_field_using_symbol_arguments", + "test_add_rename", + "test_add_table", + "test_add_table_with_decimals", + "test_change_column", + "test_change_column_default", + "test_change_column_default_to_null", + "test_change_column_nullability", + "test_change_column_quotes_column_names", + "test_change_column_with_new_default", + "test_change_column_with_nil_default", + "test_change_type_of_not_null_column", + "test_create_table_adds_id", + "test_create_table_with_binary_column", + "test_create_table_with_custom_sequence_name", + "test_create_table_with_defaults", + "test_create_table_with_force_true_does_not_drop_nonexisting_table", + "test_create_table_with_limits", + "test_create_table_with_not_null_column", + "test_create_table_with_primary_key_prefix_as_table_name", + "test_create_table_with_primary_key_prefix_as_table_name_with_underscore", + "test_create_table_with_timestamps_should_create_datetime_columns", + "test_create_table_with_timestamps_should_create_datetime_columns_with_options", + "test_create_table_without_a_block", + "test_create_table_without_id", + "test_finds_migrations", + "test_finds_pending_migrations", + "test_keeping_default_and_notnull_constraint_on_change", + "test_migrator", + "test_migrator_db_has_no_schema_migrations_table", + "test_migrator_double_down", + "test_migrator_double_up", + "test_migrator_forward", + "test_migrator_going_down_due_to_version_target", + "test_migrator_one_down", + "test_migrator_one_up", + "test_migrator_one_up_one_down", + "test_migrator_one_up_with_exception_and_rollback", + "test_migrator_rollback", + "test_migrator_verbosity", + "test_migrator_verbosity_off", + "test_migrator_with_duplicate_names", + "test_migrator_with_duplicates", + "test_migrator_with_missing_version_numbers", + "test_native_decimal_insert_manual_vs_automatic", + "test_native_types", + "test_only_loads_pending_migrations", + "test_proper_table_name", + "test_relative_migrations", + "test_remove_column_with_index", + "test_remove_column_with_multi_column_index", + "test_rename_column", + "test_rename_column_preserves_default_value_not_null", + "test_rename_column_using_symbol_arguments", + "test_rename_column_with_an_index", + "test_rename_column_with_sql_reserved_word", + "test_rename_nonexistent_column", + "test_rename_table", + "test_rename_table_for_sqlite_should_work_with_reserved_words", + "test_rename_table_with_an_index", + "test_schema_migrations_table_name", + "test_target_version_zero_should_run_only_once", + ] class PrivateToAry private diff --git a/spec/ruby/core/array/pack/buffer_spec.rb b/spec/ruby/core/array/pack/buffer_spec.rb index f1206efb3e..b77b2d1efa 100644 --- a/spec/ruby/core/array/pack/buffer_spec.rb +++ b/spec/ruby/core/array/pack/buffer_spec.rb @@ -28,6 +28,16 @@ describe "Array#pack with :buffer option" do TypeError, "buffer must be String, not Array") end + it "raise FrozenError if buffer is frozen" do + -> { [65].pack("c", buffer: "frozen-string".freeze) }.should raise_error(FrozenError) + end + + it "preserves the encoding of the given buffer" do + buffer = ''.encode(Encoding::ISO_8859_1) + [65, 66, 67].pack("ccc", buffer: buffer) + buffer.encoding.should == Encoding::ISO_8859_1 + end + context "offset (@) is specified" do it 'keeps buffer content if it is longer than offset' do n = [ 65, 66, 67 ] diff --git a/spec/ruby/library/io-wait/fixtures/classes.rb b/spec/ruby/fixtures/io.rb index 837c7edd06..87ebbbb2bd 100644 --- a/spec/ruby/library/io-wait/fixtures/classes.rb +++ b/spec/ruby/fixtures/io.rb @@ -1,12 +1,12 @@ -module IOWaitSpec +module IOSpec def self.exhaust_write_buffer(io) written = 0 buf = " " * 4096 - begin + while true written += io.write_nonblock(buf) - rescue Errno::EAGAIN, Errno::EWOULDBLOCK - return written - end while true + end + rescue Errno::EAGAIN, Errno::EWOULDBLOCK + written end end diff --git a/spec/ruby/language/case_spec.rb b/spec/ruby/language/case_spec.rb index d289eca037..464d06e46a 100644 --- a/spec/ruby/language/case_spec.rb +++ b/spec/ruby/language/case_spec.rb @@ -427,7 +427,7 @@ describe "The 'case'-construct" do :bar end RUBY - }.should complain(/warning: duplicated .when' clause with line \d+ is ignored/, verbose: true) + }.should complain(/warning: (duplicated .when' clause with line \d+ is ignored|'when' clause on line \d+ duplicates 'when' clause on line \d+ and is ignored)/, verbose: true) end end diff --git a/spec/ruby/language/pattern_matching/3.1.rb b/spec/ruby/language/pattern_matching/3.1.rb new file mode 100644 index 0000000000..7a09084e41 --- /dev/null +++ b/spec/ruby/language/pattern_matching/3.1.rb @@ -0,0 +1,75 @@ +describe "Pattern matching" do + before :each do + ScratchPad.record [] + end + + describe "Ruby 3.1 improvements" do + ruby_version_is "3.1" do + it "can omit parentheses in one line pattern matching" do + [1, 2] => a, b + [a, b].should == [1, 2] + + {a: 1} => a: + a.should == 1 + end + + it "supports pinning instance variables" do + @a = /a/ + case 'abc' + in ^@a + true + end.should == true + end + + it "supports pinning class variables" do + result = nil + Module.new do + result = module_eval(<<~RUBY) + @@a = 0..10 + + case 2 + in ^@@a + true + end + RUBY + end + + result.should == true + end + + it "supports pinning global variables" do + $a = /a/ + case 'abc' + in ^$a + true + end.should == true + end + + it "supports pinning expressions" do + case 'abc' + in ^(/a/) + true + end.should == true + + case 0 + in ^(0 + 0) + true + end.should == true + end + + it "supports pinning expressions in array pattern" do + case [3] + in [^(1 + 2)] + true + end.should == true + end + + it "supports pinning expressions in hash pattern" do + case {name: '2.6', released_at: Time.new(2018, 12, 25)} + in {released_at: ^(Time.new(2010)..Time.new(2020))} + true + end.should == true + end + end + end +end diff --git a/spec/ruby/language/pattern_matching_spec.rb b/spec/ruby/language/pattern_matching_spec.rb index a8ec078cd0..8c5df06a17 100644 --- a/spec/ruby/language/pattern_matching_spec.rb +++ b/spec/ruby/language/pattern_matching_spec.rb @@ -1,9 +1,6 @@ require_relative '../spec_helper' describe "Pattern matching" do - # TODO: Remove excessive eval calls when Ruby 3 is the minimum version. - # It is best to keep the eval's longer if other Ruby impls cannot parse pattern matching yet. - before :each do ScratchPad.record [] end @@ -11,145 +8,119 @@ describe "Pattern matching" do describe "can be standalone assoc operator that" do it "deconstructs value" do suppress_warning do - eval(<<-RUBY).should == [0, 1] - [0, 1] => [a, b] - [a, b] - RUBY + [0, 1] => [a, b] + [a, b].should == [0, 1] end end it "deconstructs value and properly scopes variables" do suppress_warning do - eval(<<-RUBY).should == [0, nil] - a = nil - eval(<<-PATTERN) - [0, 1] => [a, b] - PATTERN - [a, defined?(b)] - RUBY + a = nil + 1.times { + [0, 1] => [a, b] + } + [a, defined?(b)].should == [0, nil] end end end describe "find pattern" do it "captures preceding elements to the pattern" do - eval(<<~RUBY).should == [0, 1] - case [0, 1, 2, 3] - in [*pre, 2, 3] - pre - else - false - end - RUBY + case [0, 1, 2, 3] + in [*pre, 2, 3] + pre + else + false + end.should == [0, 1] end it "captures following elements to the pattern" do - eval(<<~RUBY).should == [2, 3] - case [0, 1, 2, 3] - in [0, 1, *post] - post - else - false - end - RUBY + case [0, 1, 2, 3] + in [0, 1, *post] + post + else + false + end.should == [2, 3] end it "captures both preceding and following elements to the pattern" do - eval(<<~RUBY).should == [[0, 1], [3, 4]] - case [0, 1, 2, 3, 4] - in [*pre, 2, *post] - [pre, post] - else - false - end - RUBY + case [0, 1, 2, 3, 4] + in [*pre, 2, *post] + [pre, post] + else + false + end.should == [[0, 1], [3, 4]] end it "can capture the entirety of the pattern" do - eval(<<~RUBY).should == [0, 1, 2, 3, 4] - case [0, 1, 2, 3, 4] - in [*everything] - everything - else - false - end - RUBY + case [0, 1, 2, 3, 4] + in [*everything] + everything + else + false + end.should == [0, 1, 2, 3, 4] end it "will match an empty Array-like structure" do - eval(<<~RUBY).should == [] - case [] - in [*everything] - everything - else - false - end - RUBY + case [] + in [*everything] + everything + else + false + end.should == [] end it "can be nested" do - eval(<<~RUBY).should == [[0, [2, 4, 6]], [[4, 16, 64]], 27] - case [0, [2, 4, 6], [3, 9, 27], [4, 16, 64]] - in [*pre, [*, 9, a], *post] - [pre, post, a] - else - false - end - RUBY + case [0, [2, 4, 6], [3, 9, 27], [4, 16, 64]] + in [*pre, [*, 9, a], *post] + [pre, post, a] + else + false + end.should == [[0, [2, 4, 6]], [[4, 16, 64]], 27] end it "can be nested with an array pattern" do - eval(<<~RUBY).should == [[4, 16, 64]] - case [0, [2, 4, 6], [3, 9, 27], [4, 16, 64]] - in [_, _, [*, 9, *], *post] - post - else - false - end - RUBY + case [0, [2, 4, 6], [3, 9, 27], [4, 16, 64]] + in [_, _, [*, 9, *], *post] + post + else + false + end.should == [[4, 16, 64]] end it "can be nested within a hash pattern" do - eval(<<~RUBY).should == [27] - case {a: [3, 9, 27]} - in {a: [*, 9, *post]} - post - else - false - end - RUBY + case {a: [3, 9, 27]} + in {a: [*, 9, *post]} + post + else + false + end.should == [27] end it "can nest hash and array patterns" do - eval(<<~RUBY).should == [42, 2] - case [0, {a: 42, b: [0, 1]}, {a: 42, b: [1, 2]}] - in [*, {a:, b: [1, c]}, *] - [a, c] - else - false - end - RUBY + case [0, {a: 42, b: [0, 1]}, {a: 42, b: [1, 2]}] + in [*, {a:, b: [1, c]}, *] + [a, c] + else + false + end.should == [42, 2] end end it "extends case expression with case/in construction" do - eval(<<~RUBY).should == :bar - case [0, 1] - in [0] - :foo - in [0, 1] - :bar - end - RUBY + case [0, 1] + in [0] + :foo + in [0, 1] + :bar + end.should == :bar end it "allows using then operator" do - eval(<<~RUBY).should == :bar - case [0, 1] - in [0] then :foo - in [0, 1] then :bar - end - RUBY + case [0, 1] + in [0] then :foo + in [0, 1] then :bar + end.should == :bar end describe "warning" do @@ -191,12 +162,10 @@ describe "Pattern matching" do end it "binds variables" do - eval(<<~RUBY).should == 1 - case [0, 1] - in [0, a] - a - end - RUBY + case [0, 1] + in [0, a] + a + end.should == 1 end it "cannot mix in and when operators" do @@ -220,47 +189,45 @@ describe "Pattern matching" do end it "checks patterns until the first matching" do - eval(<<~RUBY).should == :bar - case [0, 1] - in [0] - :foo - in [0, 1] - :bar - in [0, 1] - :baz - end - RUBY + case [0, 1] + in [0] + :foo + in [0, 1] + :bar + in [0, 1] + :baz + end.should == :bar end it "executes else clause if no pattern matches" do - eval(<<~RUBY).should == false - case [0, 1] - in [0] - true - else - false - end - RUBY + case [0, 1] + in [0] + true + else + false + end.should == false end it "raises NoMatchingPatternError if no pattern matches and no else clause" do -> { - eval <<~RUBY - case [0, 1] - in [0] - end - RUBY + case [0, 1] + in [0] + end }.should raise_error(NoMatchingPatternError, /\[0, 1\]/) + + -> { + case {a: 0, b: 1} + in a: 1, b: 1 + end + }.should raise_error(NoMatchingPatternError, /\{:a=>0, :b=>1\}/) end it "raises NoMatchingPatternError if no pattern matches and evaluates the expression only once" do evals = 0 -> { - eval <<~RUBY - case (evals += 1; [0, 1]) - in [0] - end - RUBY + case (evals += 1; [0, 1]) + in [0] + end }.should raise_error(NoMatchingPatternError, /\[0, 1\]/) evals.should == 1 end @@ -273,230 +240,185 @@ describe "Pattern matching" do true end RUBY - }.should raise_error(SyntaxError, /unexpected|expected a delimiter after the predicates of a `when` clause/) + }.should raise_error(SyntaxError, /unexpected|expected a delimiter after the patterns of an `in` clause/) end it "evaluates the case expression once for multiple patterns, caching the result" do - eval(<<~RUBY).should == true - case (ScratchPad << :foo; 1) - in 0 - false - in 1 - true - end - RUBY + case (ScratchPad << :foo; 1) + in 0 + false + in 1 + true + end.should == true ScratchPad.recorded.should == [:foo] end describe "guards" do it "supports if guard" do - eval(<<~RUBY).should == false - case 0 - in 0 if false - true - else - false - end - RUBY + case 0 + in 0 if false + true + else + false + end.should == false - eval(<<~RUBY).should == true - case 0 - in 0 if true - true - else - false - end - RUBY + case 0 + in 0 if true + true + else + false + end.should == true end it "supports unless guard" do - eval(<<~RUBY).should == false - case 0 - in 0 unless true - true - else - false - end - RUBY + case 0 + in 0 unless true + true + else + false + end.should == false - eval(<<~RUBY).should == true - case 0 - in 0 unless false - true - else - false - end - RUBY + case 0 + in 0 unless false + true + else + false + end.should == true end it "makes bound variables visible in guard" do - eval(<<~RUBY).should == true - case [0, 1] - in [a, 1] if a >= 0 - true - end - RUBY + case [0, 1] + in [a, 1] if a >= 0 + true + end.should == true end it "does not evaluate guard if pattern does not match" do - eval <<~RUBY - case 0 - in 1 if (ScratchPad << :foo) || true - else - end - RUBY + case 0 + in 1 if (ScratchPad << :foo) || true + else + end ScratchPad.recorded.should == [] end it "takes guards into account when there are several matching patterns" do - eval(<<~RUBY).should == :bar - case 0 - in 0 if false - :foo - in 0 if true - :bar - end - RUBY + case 0 + in 0 if false + :foo + in 0 if true + :bar + end.should == :bar end it "executes else clause if no guarded pattern matches" do - eval(<<~RUBY).should == false - case 0 - in 0 if false - true - else - false - end - RUBY + case 0 + in 0 if false + true + else + false + end.should == false end it "raises NoMatchingPatternError if no guarded pattern matches and no else clause" do -> { - eval <<~RUBY - case [0, 1] - in [0, 1] if false - end - RUBY + case [0, 1] + in [0, 1] if false + end }.should raise_error(NoMatchingPatternError, /\[0, 1\]/) end end describe "value pattern" do it "matches an object such that pattern === object" do - eval(<<~RUBY).should == true - case 0 - in 0 - true - end - RUBY + case 0 + in 0 + true + end.should == true - eval(<<~RUBY).should == true - case 0 - in (-1..1) - true - end - RUBY + case 0 + in ( + -1..1) + true + end.should == true - eval(<<~RUBY).should == true - case 0 - in Integer - true - end - RUBY + case 0 + in Integer + true + end.should == true - eval(<<~RUBY).should == true - case "0" - in /0/ - true - end - RUBY + case "0" + in /0/ + true + end.should == true - eval(<<~RUBY).should == true - case "0" - in ->(s) { s == "0" } - true - end - RUBY + case "0" + in -> s { s == "0" } + true + end.should == true end it "allows string literal with interpolation" do x = "x" - eval(<<~RUBY).should == true - case "x" - in "#{x + ""}" - true - end - RUBY + case "x" + in "#{x + ""}" + true + end.should == true end end describe "variable pattern" do it "matches a value and binds variable name to this value" do - eval(<<~RUBY).should == 0 - case 0 - in a - a - end - RUBY + case 0 + in a + a + end.should == 0 end it "makes bounded variable visible outside a case statement scope" do - eval(<<~RUBY).should == 0 - case 0 - in a - end + case 0 + in a + end - a - RUBY + a.should == 0 end it "create local variables even if a pattern doesn't match" do - eval(<<~RUBY).should == [0, nil, nil] - case 0 - in a - in b - in c - end + case 0 + in a + in b + in c + end - [a, b, c] - RUBY + [a, b, c].should == [0, nil, nil] end it "allow using _ name to drop values" do - eval(<<~RUBY).should == 0 - case [0, 1] - in [a, _] - a - end - RUBY + case [0, 1] + in [a, _] + a + end.should == 0 end it "supports using _ in a pattern several times" do - eval(<<~RUBY).should == true - case [0, 1, 2] - in [0, _, _] - true - end - RUBY + case [0, 1, 2] + in [0, _, _] + true + end.should == true end it "supports using any name with _ at the beginning in a pattern several times" do - eval(<<~RUBY).should == true - case [0, 1, 2] - in [0, _x, _x] - true - end - RUBY + case [0, 1, 2] + in [0, _x, _x] + true + end.should == true - eval(<<~RUBY).should == true - case {a: 0, b: 1, c: 2} - in {a: 0, b: _x, c: _x} - true - end - RUBY + case {a: 0, b: 1, c: 2} + in {a: 0, b: _x, c: _x} + true + end.should == true end it "does not support using variable name (except _) several times" do @@ -512,30 +434,24 @@ describe "Pattern matching" do it "supports existing variables in a pattern specified with ^ operator" do a = 0 - eval(<<~RUBY).should == true - case 0 - in ^a - true - end - RUBY + case 0 + in ^a + true + end.should == true end it "allows applying ^ operator to bound variables" do - eval(<<~RUBY).should == 1 - case [1, 1] - in [n, ^n] - n - end - RUBY + case [1, 1] + in [n, ^n] + n + end.should == 1 - eval(<<~RUBY).should == false - case [1, 2] - in [n, ^n] - true - else - false - end - RUBY + case [1, 2] + in [n, ^n] + true + else + false + end.should == false end it "requires bound variable to be specified in a pattern before ^ operator when it relies on a bound variable" do @@ -554,12 +470,10 @@ describe "Pattern matching" do describe "alternative pattern" do it "matches if any of patterns matches" do - eval(<<~RUBY).should == true - case 0 - in 0 | 1 | 2 - true - end - RUBY + case 0 + in 0 | 1 | 2 + true + end.should == true end it "does not support variable binding" do @@ -573,120 +487,99 @@ describe "Pattern matching" do end it "support underscore prefixed variables in alternation" do - eval(<<~RUBY).should == true - case [0, 1] - in [1, _] - false - in [0, 0] | [0, _a] - true - end - RUBY + case [0, 1] + in [1, _] + false + in [0, 0] | [0, _a] + true + end.should == true end it "can be used as a nested pattern" do - eval(<<~RUBY).should == true - case [[1], ["2"]] - in [[0] | nil, _] - false - in [[1], [1]] - false - in [[1], [2 | "2"]] - true - end - RUBY + case [[1], ["2"]] + in [[0] | nil, _] + false + in [[1], [1]] + false + in [[1], [2 | "2"]] + true + end.should == true - eval(<<~RUBY).should == true - case [1, 2] - in [0, _] | {a: 0} - false - in {a: 1, b: 2} | [1, 2] - true - end - RUBY + case [1, 2] + in [0, _] | {a: 0} + false + in {a: 1, b: 2} | [1, 2] + true + end.should == true end end describe "AS pattern" do it "binds a variable to a value if pattern matches" do - eval(<<~RUBY).should == 0 - case 0 - in Integer => n - n - end - RUBY + case 0 + in Integer => n + n + end.should == 0 end it "can be used as a nested pattern" do - eval(<<~RUBY).should == [2, 3] - case [1, [2, 3]] - in [1, Array => ary] - ary - end - RUBY + case [1, [2, 3]] + in [1, Array => ary] + ary + end.should == [2, 3] end end describe "Array pattern" do it "supports form Constant(pat, pat, ...)" do - eval(<<~RUBY).should == true - case [0, 1, 2] - in Array(0, 1, 2) - true - end - RUBY + case [0, 1, 2] + in Array(0, 1, 2) + true + end.should == true end it "supports form Constant[pat, pat, ...]" do - eval(<<~RUBY).should == true - case [0, 1, 2] - in Array[0, 1, 2] - true - end - RUBY + case [0, 1, 2] + in Array[0, 1, 2] + true + end.should == true end it "supports form [pat, pat, ...]" do - eval(<<~RUBY).should == true - case [0, 1, 2] - in [0, 1, 2] - true - end - RUBY + case [0, 1, 2] + in [0, 1, 2] + true + end.should == true end it "supports form pat, pat, ..." do - eval(<<~RUBY).should == true - case [0, 1, 2] - in 0, 1, 2 - true - end - RUBY + case [0, 1, 2] + in 0, 1, 2 + true + end.should == true - eval(<<~RUBY).should == 1 - case [0, 1, 2] - in 0, a, 2 - a - end - RUBY + case [0, 1, 2] + in 0, a, 2 + a + end.should == 1 - eval(<<~RUBY).should == [1, 2] - case [0, 1, 2] - in 0, *rest - rest - end - RUBY + case [0, 1, 2] + in 0, *rest + rest + end.should == [1, 2] end it "matches an object with #deconstruct method which returns an array and each element in array matches element in pattern" do obj = Object.new - def obj.deconstruct; [0, 1] end - eval(<<~RUBY).should == true - case obj - in [Integer, Integer] - true - end - RUBY + def obj.deconstruct + [0, 1] + end + + case obj + in [Integer, Integer] + true + end.should == true end it "calls #deconstruct once for multiple patterns, caching the result" do @@ -697,314 +590,267 @@ describe "Pattern matching" do [0, 1] end - eval(<<~RUBY).should == true - case obj - in [1, 2] - false - in [0, 1] - true - end - RUBY + case obj + in [1, 2] + false + in [0, 1] + true + end.should == true ScratchPad.recorded.should == [:deconstruct] end it "calls #deconstruct even on objects that are already an array" do obj = [1, 2] + def obj.deconstruct ScratchPad << :deconstruct [3, 4] end - eval(<<~RUBY).should == true - case obj - in [3, 4] - true - else - false - end - RUBY + case obj + in [3, 4] + true + else + false + end.should == true ScratchPad.recorded.should == [:deconstruct] end it "does not match object if Constant === object returns false" do - eval(<<~RUBY).should == false - case [0, 1, 2] - in String[0, 1, 2] - true - else - false - end - RUBY + case [0, 1, 2] + in String[0, 1, 2] + true + else + false + end.should == false end it "checks Constant === object before calling #deconstruct" do c1 = Class.new obj = c1.new obj.should_not_receive(:deconstruct) - eval(<<~RUBY).should == false - case obj - in String[1] - true - else - false - end - RUBY + + case obj + in String[1] + true + else + false + end.should == false end it "does not match object without #deconstruct method" do obj = Object.new obj.should_receive(:respond_to?).with(:deconstruct) - eval(<<~RUBY).should == false - case obj - in Object[] - true - else - false - end - RUBY + case obj + in Object[] + true + else + false + end.should == false end it "raises TypeError if #deconstruct method does not return array" do obj = Object.new - def obj.deconstruct; "" end + + def obj.deconstruct + "" + end -> { - eval <<~RUBY - case obj - in Object[] - else - end - RUBY + case obj + in Object[] + else + end }.should raise_error(TypeError, /deconstruct must return Array/) end it "accepts a subclass of Array from #deconstruct" do obj = Object.new + def obj.deconstruct Class.new(Array).new([0, 1]) end - eval(<<~RUBY).should == true - case obj - in [1, 2] - false - in [0, 1] - true - end - RUBY + case obj + in [1, 2] + false + in [0, 1] + true + end.should == true end it "does not match object if elements of array returned by #deconstruct method does not match elements in pattern" do obj = Object.new - def obj.deconstruct; [1] end - eval(<<~RUBY).should == false - case obj - in Object[0] - true - else - false - end - RUBY + def obj.deconstruct + [1] + end + + case obj + in Object[0] + true + else + false + end.should == false end it "binds variables" do - eval(<<~RUBY).should == [0, 1, 2] - case [0, 1, 2] - in [a, b, c] - [a, b, c] - end - RUBY + case [0, 1, 2] + in [a, b, c] + [a, b, c] + end.should == [0, 1, 2] end it "supports splat operator *rest" do - eval(<<~RUBY).should == [1, 2] - case [0, 1, 2] - in [0, *rest] - rest - end - RUBY + case [0, 1, 2] + in [0, *rest] + rest + end.should == [1, 2] end it "does not match partially by default" do - eval(<<~RUBY).should == false - case [0, 1, 2, 3] - in [1, 2] - true - else - false - end - RUBY + case [0, 1, 2, 3] + in [1, 2] + true + else + false + end.should == false end it "does match partially from the array beginning if list + , syntax used" do - eval(<<~RUBY).should == true - case [0, 1, 2, 3] - in [0, 1,] - true - end - RUBY + case [0, 1, 2, 3] + in [0, 1, ] + true + end.should == true - eval(<<~RUBY).should == true - case [0, 1, 2, 3] - in 0, 1,; - true - end - RUBY + case [0, 1, 2, 3] + in 0, 1,; + true + end.should == true end it "matches [] with []" do - eval(<<~RUBY).should == true - case [] - in [] - true - end - RUBY + case [] + in [] + true + end.should == true end it "matches anything with *" do - eval(<<~RUBY).should == true - case [0, 1] - in *; - true - end - RUBY + case [0, 1] + in *; + true + end.should == true end it "can be used as a nested pattern" do - eval(<<~RUBY).should == true - case [[1], ["2"]] - in [[0] | nil, _] - false - in [[1], [1]] - false - in [[1], [2 | "2"]] - true - end - RUBY + case [[1], ["2"]] + in [[0] | nil, _] + false + in [[1], [1]] + false + in [[1], [2 | "2"]] + true + end.should == true - eval(<<~RUBY).should == true - case [1, 2] - in [0, _] | {a: 0} - false - in {a: 1, b: 2} | [1, 2] - true - end - RUBY + case [1, 2] + in [0, _] | {a: 0} + false + in {a: 1, b: 2} | [1, 2] + true + end.should == true end end describe "Hash pattern" do it "supports form Constant(id: pat, id: pat, ...)" do - eval(<<~RUBY).should == true - case {a: 0, b: 1} - in Hash(a: 0, b: 1) - true - end - RUBY + case {a: 0, b: 1} + in Hash(a: 0, b: 1) + true + end.should == true end it "supports form Constant[id: pat, id: pat, ...]" do - eval(<<~RUBY).should == true - case {a: 0, b: 1} - in Hash[a: 0, b: 1] - true - end - RUBY + case {a: 0, b: 1} + in Hash[a: 0, b: 1] + true + end.should == true end it "supports form {id: pat, id: pat, ...}" do - eval(<<~RUBY).should == true - case {a: 0, b: 1} - in {a: 0, b: 1} - true - end - RUBY + case {a: 0, b: 1} + in {a: 0, b: 1} + true + end.should == true end it "supports form id: pat, id: pat, ..." do - eval(<<~RUBY).should == true - case {a: 0, b: 1} - in a: 0, b: 1 - true - end - RUBY + case {a: 0, b: 1} + in a: 0, b: 1 + true + end.should == true - eval(<<~RUBY).should == [0, 1] - case {a: 0, b: 1} - in a: a, b: b - [a, b] - end - RUBY + case {a: 0, b: 1} + in a: a, b: b + [a, b] + end.should == [0, 1] - eval(<<~RUBY).should == { b: 1, c: 2 } - case {a: 0, b: 1, c: 2} - in a: 0, **rest - rest - end - RUBY + case {a: 0, b: 1, c: 2} + in a: 0, **rest + rest + end.should == {b: 1, c: 2} end it "supports a: which means a: a" do - eval(<<~RUBY).should == [0, 1] - case {a: 0, b: 1} - in Hash(a:, b:) - [a, b] - end - RUBY + case {a: 0, b: 1} + in Hash(a:, b:) + [a, b] + end.should == [0, 1] a = b = nil - eval(<<~RUBY).should == [0, 1] - case {a: 0, b: 1} - in Hash[a:, b:] - [a, b] - end - RUBY + + case {a: 0, b: 1} + in Hash[a:, b:] + [a, b] + end.should == [0, 1] a = b = nil - eval(<<~RUBY).should == [0, 1] - case {a: 0, b: 1} - in {a:, b:} - [a, b] - end - RUBY + + case {a: 0, b: 1} + in {a:, b:} + [a, b] + end.should == [0, 1] a = nil - eval(<<~RUBY).should == [0, {b: 1, c: 2}] - case {a: 0, b: 1, c: 2} - in {a:, **rest} - [a, rest] - end - RUBY + + case {a: 0, b: 1, c: 2} + in {a:, **rest} + [a, rest] + end.should == [0, {b: 1, c: 2}] a = b = nil - eval(<<~RUBY).should == [0, 1] - case {a: 0, b: 1} - in a:, b: - [a, b] - end - RUBY + + case {a: 0, b: 1} + in a:, b: + [a, b] + end.should == [0, 1] end it "can mix key (a:) and key-value (a: b) declarations" do - eval(<<~RUBY).should == [0, 1] - case {a: 0, b: 1} - in Hash(a:, b: x) - [a, x] - end - RUBY + case {a: 0, b: 1} + in Hash(a:, b: x) + [a, x] + end.should == [0, 1] end it "supports 'string': key literal" do - eval(<<~RUBY).should == true - case {a: 0} - in {"a": 0} - true - end - RUBY + case {a: 0} + in {"a": 0} + true + end.should == true end it "does not support non-symbol keys" do @@ -1018,8 +864,6 @@ describe "Pattern matching" do end it "does not support string interpolation in keys" do - x = "a" - -> { eval <<~'RUBY' case {a: 1} @@ -1041,14 +885,15 @@ describe "Pattern matching" do it "matches an object with #deconstruct_keys method which returns a Hash with equal keys and each value in Hash matches value in pattern" do obj = Object.new - def obj.deconstruct_keys(*); {a: 1} end - eval(<<~RUBY).should == true - case obj - in {a: 1} - true - end - RUBY + def obj.deconstruct_keys(*) + {a: 1} + end + + case obj + in {a: 1} + true + end.should == true end it "calls #deconstruct_keys per pattern" do @@ -1059,96 +904,92 @@ describe "Pattern matching" do {a: 1} end - eval(<<~RUBY).should == true - case obj - in {b: 1} - false - in {a: 1} - true - end - RUBY + case obj + in {b: 1} + false + in {a: 1} + true + end.should == true ScratchPad.recorded.should == [:deconstruct_keys, :deconstruct_keys] end it "does not match object if Constant === object returns false" do - eval(<<~RUBY).should == false - case {a: 1} - in String[a: 1] - true - else - false - end - RUBY + case {a: 1} + in String[a: 1] + true + else + false + end.should == false end it "checks Constant === object before calling #deconstruct_keys" do c1 = Class.new obj = c1.new obj.should_not_receive(:deconstruct_keys) - eval(<<~RUBY).should == false - case obj - in String(a: 1) - true - else - false - end - RUBY + + case obj + in String(a: 1) + true + else + false + end.should == false end it "does not match object without #deconstruct_keys method" do obj = Object.new obj.should_receive(:respond_to?).with(:deconstruct_keys) - eval(<<~RUBY).should == false - case obj - in Object[a: 1] - true - else - false - end - RUBY + case obj + in Object[a: 1] + true + else + false + end.should == false end it "does not match object if #deconstruct_keys method does not return Hash" do obj = Object.new - def obj.deconstruct_keys(*); "" end + + def obj.deconstruct_keys(*) + "" + end -> { - eval <<~RUBY - case obj - in Object[a: 1] - end - RUBY + case obj + in Object[a: 1] + end }.should raise_error(TypeError, /deconstruct_keys must return Hash/) end it "does not match object if #deconstruct_keys method returns Hash with non-symbol keys" do obj = Object.new - def obj.deconstruct_keys(*); {"a" => 1} end - eval(<<~RUBY).should == false - case obj - in Object[a: 1] - true - else - false - end - RUBY + def obj.deconstruct_keys(*) + {"a" => 1} + end + + case obj + in Object[a: 1] + true + else + false + end.should == false end it "does not match object if elements of Hash returned by #deconstruct_keys method does not match values in pattern" do obj = Object.new - def obj.deconstruct_keys(*); {a: 1} end - eval(<<~RUBY).should == false - case obj - in Object[a: 2] - true - else - false - end - RUBY + def obj.deconstruct_keys(*) + {a: 1} + end + + case obj + in Object[a: 2] + true + else + false + end.should == false end it "passes keys specified in pattern as arguments to #deconstruct_keys method" do @@ -1159,11 +1000,9 @@ describe "Pattern matching" do {a: 1, b: 2, c: 3} end - eval <<~RUBY - case obj - in Object[a: 1, b: 2, c: 3] - end - RUBY + case obj + in Object[a: 1, b: 2, c: 3] + end ScratchPad.recorded.sort.should == [[[:a, :b, :c]]] end @@ -1176,11 +1015,9 @@ describe "Pattern matching" do {a: 1, b: 2, c: 3} end - eval <<~RUBY - case obj - in Object[a: 1, b: 2, **] - end - RUBY + case obj + in Object[a: 1, b: 2, **] + end ScratchPad.recorded.sort.should == [[[:a, :b]]] end @@ -1193,131 +1030,105 @@ describe "Pattern matching" do {a: 1, b: 2} end - eval <<~RUBY - case obj - in Object[a: 1, **rest] - end - RUBY + case obj + in Object[a: 1, **rest] + end ScratchPad.recorded.should == [[nil]] end it "binds variables" do - eval(<<~RUBY).should == [0, 1, 2] - case {a: 0, b: 1, c: 2} - in {a: x, b: y, c: z} - [x, y, z] - end - RUBY + case {a: 0, b: 1, c: 2} + in {a: x, b: y, c: z} + [x, y, z] + end.should == [0, 1, 2] end it "supports double splat operator **rest" do - eval(<<~RUBY).should == {b: 1, c: 2} - case {a: 0, b: 1, c: 2} - in {a: 0, **rest} - rest - end - RUBY + case {a: 0, b: 1, c: 2} + in {a: 0, **rest} + rest + end.should == {b: 1, c: 2} end it "treats **nil like there should not be any other keys in a matched Hash" do - eval(<<~RUBY).should == true - case {a: 1, b: 2} - in {a: 1, b: 2, **nil} - true - end - RUBY + case {a: 1, b: 2} + in {a: 1, b: 2, **nil} + true + end.should == true - eval(<<~RUBY).should == false - case {a: 1, b: 2} - in {a: 1, **nil} - true - else - false - end - RUBY + case {a: 1, b: 2} + in {a: 1, **nil} + true + else + false + end.should == false end it "can match partially" do - eval(<<~RUBY).should == true - case {a: 1, b: 2} - in {a: 1} - true - end - RUBY + case {a: 1, b: 2} + in {a: 1} + true + end.should == true end it "matches {} with {}" do - eval(<<~RUBY).should == true - case {} - in {} - true - end - RUBY + case {} + in {} + true + end.should == true end it "in {} only matches empty hashes" do - eval(<<~RUBY).should == false - case {a: 1} - in {} - true - else - false - end - RUBY + case {a: 1} + in {} + true + else + false + end.should == false end it "in {**nil} only matches empty hashes" do - eval(<<~RUBY).should == true - case {} - in {**nil} - true - else - false - end - RUBY + case {} + in {**nil} + true + else + false + end.should == true - eval(<<~RUBY).should == false - case {a: 1} - in {**nil} - true - else - false - end - RUBY + case {a: 1} + in {**nil} + true + else + false + end.should == false end it "matches anything with **" do - eval(<<~RUBY).should == true - case {a: 1} - in **; - true - end - RUBY + case {a: 1} + in **; + true + end.should == true end it "can be used as a nested pattern" do - eval(<<~RUBY).should == true - case {a: {a: 1, b: 1}, b: {a: 1, b: 2}} - in {a: {a: 0}} - false - in {a: {a: 1}, b: {b: 1}} - false - in {a: {a: 1}, b: {b: 2}} - true - end - RUBY + case {a: {a: 1, b: 1}, b: {a: 1, b: 2}} + in {a: {a: 0}} + false + in {a: {a: 1}, b: {b: 1}} + false + in {a: {a: 1}, b: {b: 2} } + true + end.should == true - eval(<<~RUBY).should == true - case [{a: 1, b: [1]}, {a: 1, c: ["2"]}] - in [{a:, c:},] - false - in [{a: 1, b:}, {a: 1, c: [Integer]}] - false - in [_, {a: 1, c: [String]}] - true - end - RUBY + case [{a: 1, b: [1]}, {a: 1, c: ["2"]}] + in [{a:, c:}, ] + false + in [{a: 1, b:}, {a: 1, c: [Integer]}] + false + in [_, {a: 1, c: [String]}] + true + end.should == true end end @@ -1335,12 +1146,11 @@ describe "Pattern matching" do Module.new do using refinery - result = eval(<<~RUBY) + result = case [] in [0] true end - RUBY end result.should == true @@ -1359,12 +1169,11 @@ describe "Pattern matching" do Module.new do using refinery - result = eval(<<~RUBY) + result = case {} in a: 0 true end - RUBY end result.should == true @@ -1383,127 +1192,18 @@ describe "Pattern matching" do Module.new do using refinery - result = eval(<<~RUBY) + result = case {} in Array true end - RUBY end result.should == true end end +end - describe "Ruby 3.1 improvements" do - ruby_version_is "3.1" do - it "can omit parentheses in one line pattern matching" do - eval(<<~RUBY).should == [1, 2] - [1, 2] => a, b - [a, b] - RUBY - - eval(<<~RUBY).should == 1 - {a: 1} => a: - a - RUBY - end - - it "supports pinning instance variables" do - eval(<<~RUBY).should == true - @a = /a/ - case 'abc' - in ^@a - true - end - RUBY - end - - it "supports pinning class variables" do - result = nil - Module.new do - result = module_eval(<<~RUBY) - @@a = 0..10 - - case 2 - in ^@@a - true - end - RUBY - end - - result.should == true - end - - it "supports pinning global variables" do - eval(<<~RUBY).should == true - $a = /a/ - case 'abc' - in ^$a - true - end - RUBY - end - - it "supports pinning expressions" do - eval(<<~RUBY).should == true - case 'abc' - in ^(/a/) - true - end - RUBY - - eval(<<~RUBY).should == true - case 0 - in ^(0+0) - true - end - RUBY - end - - it "supports pinning expressions in array pattern" do - eval(<<~RUBY).should == true - case [3] - in [^(1+2)] - true - end - RUBY - end - - it "supports pinning expressions in hash pattern" do - eval(<<~RUBY).should == true - case {name: '2.6', released_at: Time.new(2018, 12, 25)} - in {released_at: ^(Time.new(2010)..Time.new(2020))} - true - end - RUBY - end - end - end - - describe "value in pattern" do - it "returns true if the pattern matches" do - eval("1 in 1").should == true - - eval("1 in Integer").should == true - - e = nil - eval("[1, 2] in [1, e]").should == true - e.should == 2 - - k = nil - eval("{k: 1} in {k:}").should == true - k.should == 1 - end - - it "returns false if the pattern does not match" do - eval("1 in 2").should == false - - eval("1 in Float").should == false - - eval("[1, 2] in [2, e]").should == false - - eval("{k: 1} in {k: 2}").should == false - end - end +ruby_version_is "3.1" do + require_relative 'pattern_matching/3.1' end diff --git a/spec/ruby/library/io-wait/wait_readable_spec.rb b/spec/ruby/library/io-wait/wait_readable_spec.rb index 06ffbda5c8..df25fdb931 100644 --- a/spec/ruby/library/io-wait/wait_readable_spec.rb +++ b/spec/ruby/library/io-wait/wait_readable_spec.rb @@ -24,4 +24,23 @@ describe "IO#wait_readable" do it "waits for the IO to become readable with the given large timeout" do @io.wait_readable(365 * 24 * 60 * 60).should == @io end + + it "can be interrupted" do + rd, wr = IO.pipe + start = Process.clock_gettime(Process::CLOCK_MONOTONIC) + + t = Thread.new do + rd.wait_readable(10) + end + + Thread.pass until t.stop? + t.kill + t.join + + finish = Process.clock_gettime(Process::CLOCK_MONOTONIC) + (finish - start).should < 9 + ensure + rd.close + wr.close + end end diff --git a/spec/ruby/library/io-wait/wait_spec.rb b/spec/ruby/library/io-wait/wait_spec.rb index fc07c6a8d9..5952f127f7 100644 --- a/spec/ruby/library/io-wait/wait_spec.rb +++ b/spec/ruby/library/io-wait/wait_spec.rb @@ -1,5 +1,5 @@ require_relative '../../spec_helper' -require_relative 'fixtures/classes' +require_relative '../../fixtures/io' ruby_version_is ''...'3.2' do require 'io/wait' @@ -55,7 +55,7 @@ describe "IO#wait" do end it "waits for the WRITABLE event to be ready" do - written_bytes = IOWaitSpec.exhaust_write_buffer(@w) + written_bytes = IOSpec.exhaust_write_buffer(@w) @w.wait(IO::WRITABLE, 0).should == nil @r.read(written_bytes) @@ -67,7 +67,7 @@ describe "IO#wait" do end it "returns nil when the WRITABLE event is not ready during the timeout" do - IOWaitSpec.exhaust_write_buffer(@w) + IOSpec.exhaust_write_buffer(@w) @w.wait(IO::WRITABLE, 0).should == nil end @@ -92,7 +92,7 @@ describe "IO#wait" do end it "changes thread status to 'sleep' when waits for WRITABLE event" do - written_bytes = IOWaitSpec.exhaust_write_buffer(@w) + IOSpec.exhaust_write_buffer(@w) t = Thread.new { @w.wait(IO::WRITABLE, 10) } sleep 1 @@ -100,6 +100,37 @@ describe "IO#wait" do t.kill t.join # Thread#kill doesn't wait for the thread to end end + + it "can be interrupted when waiting for READABLE event" do + start = Process.clock_gettime(Process::CLOCK_MONOTONIC) + + t = Thread.new do + @r.wait(IO::READABLE, 10) + end + + Thread.pass until t.stop? + t.kill + t.join # Thread#kill doesn't wait for the thread to end + + finish = Process.clock_gettime(Process::CLOCK_MONOTONIC) + (finish - start).should < 9 + end + + it "can be interrupted when waiting for WRITABLE event" do + IOSpec.exhaust_write_buffer(@w) + start = Process.clock_gettime(Process::CLOCK_MONOTONIC) + + t = Thread.new do + @w.wait(IO::WRITABLE, 10) + end + + Thread.pass until t.stop? + t.kill + t.join # Thread#kill doesn't wait for the thread to end + + finish = Process.clock_gettime(Process::CLOCK_MONOTONIC) + (finish - start).should < 9 + end end context "[timeout, mode] passed" do diff --git a/spec/ruby/library/io-wait/wait_writable_spec.rb b/spec/ruby/library/io-wait/wait_writable_spec.rb index 8c44780d39..8639dd717b 100644 --- a/spec/ruby/library/io-wait/wait_writable_spec.rb +++ b/spec/ruby/library/io-wait/wait_writable_spec.rb @@ -1,4 +1,5 @@ require_relative '../../spec_helper' +require_relative '../../fixtures/io' ruby_version_is ''...'3.2' do require 'io/wait' @@ -17,4 +18,24 @@ describe "IO#wait_writable" do # Represents one year and is larger than a 32-bit int STDOUT.wait_writable(365 * 24 * 60 * 60).should == STDOUT end + + it "can be interrupted" do + rd, wr = IO.pipe + IOSpec.exhaust_write_buffer(wr) + start = Process.clock_gettime(Process::CLOCK_MONOTONIC) + + t = Thread.new do + wr.wait_writable(10) + end + + Thread.pass until t.stop? + t.kill + t.join + + finish = Process.clock_gettime(Process::CLOCK_MONOTONIC) + (finish - start).should < 9 + ensure + rd.close unless rd.closed? + wr.close unless wr.closed? + end end diff --git a/spec/ruby/library/rbconfig/rbconfig_spec.rb b/spec/ruby/library/rbconfig/rbconfig_spec.rb index b90cc90970..8e38227c21 100644 --- a/spec/ruby/library/rbconfig/rbconfig_spec.rb +++ b/spec/ruby/library/rbconfig/rbconfig_spec.rb @@ -88,6 +88,30 @@ describe 'RbConfig::CONFIG' do end end end + + guard -> { %w[aarch64 arm64].include? RbConfig::CONFIG['host_cpu'] } do + it "['host_cpu'] returns CPU architecture properly for AArch64" do + platform_is :darwin do + RbConfig::CONFIG['host_cpu'].should == 'arm64' + end + + platform_is_not :darwin do + RbConfig::CONFIG['host_cpu'].should == 'aarch64' + end + end + end + + guard -> { platform_is(:linux) || platform_is(:darwin) } do + it "['host_os'] returns a proper OS name or platform" do + platform_is :darwin do + RbConfig::CONFIG['host_os'].should.match?(/darwin/) + end + + platform_is :linux do + RbConfig::CONFIG['host_os'].should.match?(/linux/) + end + end + end end describe "RbConfig::TOPDIR" do @@ -99,3 +123,32 @@ describe "RbConfig::TOPDIR" do end end end + +describe "RUBY_PLATFORM" do + it "RUBY_PLATFORM contains a proper CPU architecture" do + RUBY_PLATFORM.should.include? RbConfig::CONFIG['host_cpu'] + end + + guard -> { platform_is(:linux) || platform_is(:darwin) } do + it "RUBY_PLATFORM contains OS name" do + # don't use RbConfig::CONFIG['host_os'] as far as it could be slightly different, e.g. linux-gnu + platform_is(:linux) do + RUBY_PLATFORM.should.include? 'linux' + end + + platform_is(:darwin) do + RUBY_PLATFORM.should.include? 'darwin' + end + end + end +end + +describe "RUBY_DESCRIPTION" do + it "contains version" do + RUBY_DESCRIPTION.should.include? RUBY_VERSION + end + + it "contains RUBY_PLATFORM" do + RUBY_DESCRIPTION.should.include? RUBY_PLATFORM + end +end diff --git a/spec/ruby/optional/capi/io_spec.rb b/spec/ruby/optional/capi/io_spec.rb index 870abef3ea..bdec46f5e1 100644 --- a/spec/ruby/optional/capi/io_spec.rb +++ b/spec/ruby/optional/capi/io_spec.rb @@ -1,4 +1,5 @@ require_relative 'spec_helper' +require_relative '../../fixtures/io' load_extension('io') @@ -279,6 +280,22 @@ describe "C-API IO function" do it "raises an IOError if the IO is not initialized" do -> { @o.rb_io_maybe_wait_writable(0, IO.allocate, nil) }.should raise_error(IOError, "uninitialized stream") end + + it "can be interrupted" do + IOSpec.exhaust_write_buffer(@w_io) + start = Process.clock_gettime(Process::CLOCK_MONOTONIC) + + t = Thread.new do + @o.rb_io_maybe_wait_writable(0, @w_io, 10) + end + + Thread.pass until t.stop? + t.kill + t.join + + finish = Process.clock_gettime(Process::CLOCK_MONOTONIC) + (finish - start).should < 9 + end end end @@ -355,6 +372,21 @@ describe "C-API IO function" do thr.join end + it "can be interrupted" do + start = Process.clock_gettime(Process::CLOCK_MONOTONIC) + + t = Thread.new do + @o.rb_io_maybe_wait_readable(0, @r_io, 10, false) + end + + Thread.pass until t.stop? + t.kill + t.join + + finish = Process.clock_gettime(Process::CLOCK_MONOTONIC) + (finish - start).should < 9 + end + it "raises an IOError if the IO is closed" do @r_io.close -> { @o.rb_io_maybe_wait_readable(0, @r_io, nil, false) }.should raise_error(IOError, "closed stream") @@ -438,6 +470,37 @@ describe "C-API IO function" do it "raises an IOError if the IO is not initialized" do -> { @o.rb_io_maybe_wait(0, IO.allocate, IO::WRITABLE, nil) }.should raise_error(IOError, "uninitialized stream") end + + it "can be interrupted when waiting for READABLE event" do + start = Process.clock_gettime(Process::CLOCK_MONOTONIC) + + t = Thread.new do + @o.rb_io_maybe_wait(0, @r_io, IO::READABLE, 10) + end + + Thread.pass until t.stop? + t.kill + t.join + + finish = Process.clock_gettime(Process::CLOCK_MONOTONIC) + (finish - start).should < 9 + end + + it "can be interrupted when waiting for WRITABLE event" do + IOSpec.exhaust_write_buffer(@w_io) + start = Process.clock_gettime(Process::CLOCK_MONOTONIC) + + t = Thread.new do + @o.rb_io_maybe_wait(0, @w_io, IO::WRITABLE, 10) + end + + Thread.pass until t.stop? + t.kill + t.join + + finish = Process.clock_gettime(Process::CLOCK_MONOTONIC) + (finish - start).should < 9 + end end end |